2

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 debug org.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 the fetchType 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?

1 Answer 1

2

There are two main strategies working with hibernate.

  • long lived session (keep db-session paralell to http-session)
  • short lived session (throwing LazyInitializationException after conn-close)

The LazyInitializationException gives us the hint that you are using short-lived-session.

Either you use long lived session and use the entities in the view of the MVC. Or you remap all values from the entities to new wrappers who are not entities.

I would suggest to keep the short-lived-session strategie and remap all values to pojos for the view. Because: The db-structure is rarely the information-structure you need in the frontend. In example:

You have two Entitys:

  • User
    • long UserId
    • String Firstname
    • String Lastname
  • Address
    • long AddressId
    • String Street
    • String Zip
    • String City
    • long UserId

But in the View you have this Dataobject (DTO):

  • class User
    • String Firstname
    • String Lastname
    • String Street
    • String Zip
    • String City

You see, the beans are different and you will not get a LazyInitializationException after conn-close.

8
  • So essentially, utilize DTO's? Commented Aug 15, 2016 at 13:08
  • Between view and controller only, yes.
    – Grim
    Commented Aug 15, 2016 at 13:09
  • Long lived sessions are essentially the open-in-view pattern, or did you have something else in mind?
    – Deroude
    Commented Aug 15, 2016 at 13:09
  • @Deroude No, long-lived-sessions are db-sessions that are about 2hours open for read and write. Theese db-sessions are bound to the entities. Yes, on save you must load the entity from DB, change values and write back to db.
    – Grim
    Commented Aug 15, 2016 at 13:11
  • Also, for DTOs, if you want to save them using the ORM, you still need to reload the entity and then save it back -- the alternative being to use an update query (either JPQL or native) -- but it kind of kills the whole reason why we use ORMs, doesn't it?
    – Deroude
    Commented Aug 15, 2016 at 13:11

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.