16

There are countless questions here, how to solve the "could not initialize proxy" problem via eager fetching, keeping the transaction open, opening another one, OpenEntityManagerInViewFilter, and whatever.

But is it possible to simply tell Hibernate to ignore the problem and pretend the collection is empty? In my case, not fetching it before simply means that I don't care.

This is actually an XY problem with the following Y:

I'm having classes like

class Detail {
    @ManyToOne(optional=false) Master master;
    ...
}

class Master {
    @OneToMany(mappedBy="master") List<Detail> details;
    ...
}

and want to serve two kinds of requests: One returning a single master with all its details and another one returning a list of masters without details. The result gets converted to JSON by Gson.

I've tried session.clear and session.evict(master), but they don't touch the proxy used in place of details. What worked was

 master.setDetails(nullOrSomeCollection)

which feels rather hacky. I'd prefer the "ignorance" as it'd be applicable generally without knowing what parts of what are proxied.

Writing a Gson TypeAdapter ignoring instances of AbstractPersistentCollection with initialized=false could be a way, but this would depend on org.hibernate.collection.internal, which is surely no good thing. Catching the exception in the TypeAdapter doesn't sound much better.

Update after some answers

My goal is not to "get the data loaded instead of the exception", but "how to get null instead of the exception" I

Dragan raises a valid point that forgetting to fetch and returning a wrong data would be much worse than an exception. But there's an easy way around it:

  • do this for collections only
  • never use null for them
  • return null rather than an empty collection as an indication of unfetched data

This way, the result can never be wrongly interpreted. Should I ever forget to fetch something, the response will contain null which is invalid.

Community
  • 1
  • 1
maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • 1
    did you read my solution http://stackoverflow.com/questions/6354265/using-jpa-entities-in-jsf-which-is-the-best-strategy-to-prevent-lazyinitializat/32046337#32046337 – Nassim MOUALEK Sep 09 '15 at 14:12
  • @NassimMOUALEK Yes, I did. It also solve the other problem, namely "how to get the data loaded instead of the exception", but what I what is "how to get null instead instead of the exception". – maaartinus Sep 09 '15 at 19:11
  • i thought may be it could help you to change your strategy, and you should read this http://stackoverflow.com/a/32039605/3252285 – Nassim MOUALEK Sep 09 '15 at 19:51
  • _"Should I ever forget to fetch something, the response will contain null which is invalid."_ But how will you differentiate between deliberately-unfetched and forgot-to-fetch collections? `null` is returned for both. – Dragan Bozanovic Sep 10 '15 at 13:39
  • @DraganBozanovic In the server not at all, it just doesn't care: From its POV, if it fetches a field or not, and it's always correct, never forgotten. +++ A Java client would have an object looking like the DTO I'm refusing to use in the main code and do a non-null check. This should be done in tests, too. +++ A Javascript client would simply blow when seeing a null instead of an collection. An integration test would show this. – maaartinus Sep 10 '15 at 23:18
  • @DraganBozanovic So, I'm basically simplifying the *main* code by eliminating the DTO and making fetching/non-fetching of the data to the single source of truth. This is IMHO good: smaller, faster, simpler, no two things to be kept in sync. +++ At the same it must be tested, so the *test* code needs some DTO or other description of what should be fetched. This also allows to test that nothing superfluous gets fetched, and that's an information which would otherwise get lost when building the DTO in the main code. I haven't tried it yet, what do you think? – maaartinus Sep 10 '15 at 23:29
  • Ok, that eliminates the potential `null` ambiguity. Basically, by initializing only what you need you are making a kind of 'invisible' DTO on the server side that contains only the initialized state of the entity. Am I correct? If so, then I personally find your approach interesting. – Dragan Bozanovic Sep 11 '15 at 10:54
  • However, when you need to provide more details than contained in the entity by combining data from more entities or adapting them to the service consumer, then you will have to fall back to DTOs anyway. That's why I always prefer DTOs to be consistent in all use cases. Of course, you know your system architecture better; if DTOs would produce really lots of boilerplate, then you should go with the alternatives – Dragan Bozanovic Sep 11 '15 at 10:59
  • @DraganBozanovic Yes, invisible DTO given by what gets fetched, instead of writing a class. The invisible DTO is actually always there, the standard approach adds a visible one. I guess, I'll try it out. +++ Combining multiple entities can be done by something like `Pair` (I'm no fan of `Pair`), where both entities are stripped down by what gets fetched. This feels hacky and maybe even too hacky. Adapting to the service consumer is not needed if I write the consumer, too. Agreed that consistency is even more important than avoiding redundancy (which is what I'm trying to do here). – maaartinus Sep 11 '15 at 11:10

5 Answers5

5

You could utilize Hibernate.isInitialized, which is part of the Hibernate public API.

So, in the TypeAdapter you can add something like this:

if ((value instanceof Collection) && !Hibernate.isInitialized(value)) {
   result = new ArrayList();
}

However, in my modest opinion your approach in general is not the way to go.

"In my case, not fetching it before simply means that I don't care."

Or it means you forgot to fetch it and now you are returning wrong data (worse than getting the exception; the consumer of the service thinks the collection is empty, but it is not).

I would not like to propose "better" solutions (it is not topic of the question and each approach has its own advantages), but the way that I solve issues like these in most use cases (and it is one of the ways commonly adopted) is using DTOs: Simply define a DTO that represents the response of the service, fill it in the transactional context (no LazyInitializationExceptions there) and give it to the framework that will transform it to the service response (json, xml, etc).

Dragan Bozanovic
  • 23,102
  • 5
  • 43
  • 110
  • `isInitialized` is fine, but I'd have to apply it to all fields... or maybe just register a `TypeAdapterFactory`! Cool! +++ Concerning returning wrong data, I agree with you, but there's a remedy (not perfect: the server returns an invalid answer, but the client detects the problem). Sure, DTO would work, but I consider it to be a [boilerplate](http://stackoverflow.com/a/1440994/581205) (though possibly necessary evil in big projects). – maaartinus Sep 09 '15 at 19:34
  • Sure, don't do it for each field separately, it would defeat the purpose. Register a custom serializer. – Dragan Bozanovic Sep 09 '15 at 19:51
  • I'll accept your answer when I try it out (next week, probably). – maaartinus Sep 15 '15 at 06:54
2

What you can try is a solution like the following.

Creating an interface named LazyLoader

@FunctionalInterface // Java 8
public interface LazyLoader<T> {
    void load(T t);
}

And in your Service

public class Service {
    List<Master> getWithDetails(LazyLoader<Master> loader) {
        // Code to get masterList from session
        for(Master master:masterList) {
            loader.load(master);
        }        
    }
}

And call this service like below

Service.getWithDetails(new LazyLoader<Master>() {
    public void load(Master master) {
        for(Detail detail:master.getDetails()) {
            detail.getId(); // This will load detail
        }
    }
});

And in Java 8 you can use Lambda as it is a Single Abstract Method (SAM).

Service.getWithDetails((master) -> {
    for(Detail detail:master.getDetails()) {
        detail.getId(); // This will load detail
    }
});

You can use the solution above with session.clear and session.evict(master)

shazin
  • 21,379
  • 3
  • 54
  • 71
  • It looks like you're solving the problem "how to get the details", but my problem is "how to get `Master` without details and **without proxies throwing**". Neither `session.clear()` nor `session.evict(master)` seem to help with this (the proxy-devil is still in the `details` greedy to throw when I ask it anything). – maaartinus Sep 09 '15 at 05:19
2

I have raised a similar question in the past (why dependent collection isn't evicted when parent entity is), and it has resulted an answer which you could try for your case.

Community
  • 1
  • 1
mindas
  • 26,463
  • 15
  • 97
  • 154
  • I can't see how to apply it, but it's just my lacking knowledge. I actually believe, that [Dragan's answer](http://stackoverflow.com/a/32481321/581205) solves it, I just haven't tried yet. At the same time, I'm sure that I'll get to deal with caching soon, so I'm bookmarking your question. – maaartinus Sep 10 '15 at 23:47
  • You said you "session.evict(master), but they don't touch the proxy used in place of details". My question talks about evicting the dependent collection ("details" in your case). – mindas Sep 11 '15 at 07:01
  • I guess, that evicting was a bad idea of mine, but thank you, now I do understand. – maaartinus Sep 11 '15 at 08:55
1

The solution for this is to use queries instead of associations (one-to-many or many-to-many). Even one of the original authors of Hibernate said that Collections are a feature and not an end-goal.

In your case you can get better flexibility of removing the collections mapping and simply fetch the associated relations when you need them in your data access layer.

Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
1

You could create a Java proxy for every entity, so that every method is surrounded by a try/catch block that returns null when a LazyInitializationException is catched.

For this to work, all your entities would need to implement an interface and you'd need to reference this interface (instead of the entity class) all throughout your program.

If you can't (or just don't want) to use interfaces, then you could try to build a dynamic proxy with javassist or cglib, or even manually, as explained in this article.

If you go by common Java proxies, here's a sketch:

public static <T> T ignoringLazyInitialization(
    final Object entity, 
    final Class<T> entityInterface) {

    return (T) Proxy.newProxyInstance(
        entityInterface.getClassLoader(),
        new Class[] { entityInterface },
        new InvocationHandler() {

            @Override
            public Object invoke(
                Object proxy, 
                Method method, 
                Object[] args) 
                throws Throwable {

                try {
                    return method.invoke(entity, args);
                } catch (InvocationTargetException e) {
                    Throwable cause = e.getTargetException();
                    if (cause instanceof LazyInitializationException) {
                        return null;
                    }
                    throw cause;
                }
            }
        });
}

So, if you have an entity A as follows:

public interface A {

    // getters & setters and other methods DEFINITIONS

}

with its implementation:

public class AImpl implements A {

    // getters & setters and other methods IMPLEMENTATIONS

}

Then, assuming you have a reference to the entity class (as returned by Hibernate), you could create a proxy as follows:

AImpl entityAImpl = ...; // some query, load, etc

A entityA = ignoringLazyInitialization(entityAImpl, A.class);

NOTE 1: You'd need to proxy collections returned by Hibernate as well (left as an excersice to the reader) ;)

NOTE 2: Ideally, you should do all this proxying stuff in a DAO or in some type of facade, so that everything is transparent to the user of the entities

NOTE 3: This is by no means optimal, since it creates a stacktrace for every access to an non-initialized field

NOTE 4: This works, but adds complexity; consider if it's really necessary.

fps
  • 33,623
  • 8
  • 55
  • 110