I know this has been discussed over and over (I have several implementations of my own on this matter), perhaps that is part of the reason I am asking: there is no definitive answer (after hours of searching) and I feel this is catch 22.
My setting is Spring Boot 1.3.6, which is "married" to Hibernate 4.3.11. I am using Spring Data JPA.
I have an entity which contains multiple collections of sub-entities, @OneToMany
and @ManyToMany
. That's the model -- it's not for me to love or hate.
Then I have a normal CRUD UI with a table of the main entity and an editor where the user gets to choose options for the sub-entities and save the main entity.
The UI is done with Vaadin and there are Web Sockets involved. So - not a normal Http exchange.
With the normal settings I get the "normal" LazyInitializationException from Hibernate. Because, once the entity is loaded in the table screen, the session is closed, so, when the collections are called in the editor screen, either I reload the entity altogether, which kind of kills all the mojo of using an ORM, or I try something else:
- make the collections EAGER (and drag them with me everywhere, not to mention the n+1 problem which happens despite the
@Fetch(FetchMode=JOIN)
which shouldn't have been there in the first place) - add the
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
to my startup config, which is basically the same as the first option, turned into a default. No, thank you. - use Hibernate.initialize on the collections just after the parent entity is retrieved (i.e. in the same session), which is something like programmatic EAGER retrieval -- but it's highly coupled with Hibernate and it doesn't save me the extra query (n+1).
- retrieve the data with "fetch join" in my JPQL query - except this can cause the result set to escalate to thousands of rows (which are then aggregated in memory), not to mention A) it requires JPQL for otherwise trivial queries, implemented with simple interface names in Spring Data and B) if you want pagination, you need to write the
countingQuery
also (without join fetch), because otherwise you will get a nice, clear and easy to debugorg.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list
. Also, if you "forget" to remove thefetchType
options on the@OneToMany
or@ManyToMany
annotations, you get another hair-pulling exception:org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
- create a filter following the open-in-view pattern (or rather anti-pattern), which would retrieve the same EntityManager for all requests in the same, well, something -- not the Http Session, because we use Web Sockets (or pretty much anything else, Hibernate is not supposed to be a Web ORM); not the Vaadin Session, because I may choose to have multiple servlets, or distribute the entire application; or stop using Vaadin; not a thread local, because I may choose to perform operations in different thread pools, not inherited from the thread local: again, this is not Hibernate's concern.
Finally, switching to EclipseLink, which has the more sensible approach of simply requesting the sub-collections when needed, in a new session, seems more of a chore than Spring Boot usually offers (with several discussions still ongoing regarding the necessity to run the application with the instrumentation agent).
So, ultimately, is there a hack-free solution for this problem?