Search code examples
javahibernatejpaormhibernate-mapping

Solving LazyInitializationException via ignorance


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.


Solution

  • 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).