54

I have been suffering from infamous hibernate exception

org.hibernate.LazyInitializationException: could not initialize proxy - no Session

Now the community is cheering over

<property name="hibernate.enable_lazy_load_no_trans" value="true"/>

saying it solves the problem but USE IT WITH CAUTION.

What they mean by use it with caution? What this property actually does?

Please give me any insights. Thanks in advance.

Sachin Verma
  • 3,334
  • 9
  • 31
  • 66

4 Answers4

45

The problem with this approach is that you can have the N+1 effect.

Imagine that you have the following entity:

public class Person{
    @OneToMany // default to lazy
    private List<Order> orderList;
}

If you have a report that returns 10K of persons, and if in this report you execute the code person.getOrderList() the JPA/Hibernate will execute 10K of queries. This is the N+1 effect, you will have no control about all the queries that will be executed.

Imagine now that Order is like below:

public class Order{
    @OneToMany // default to lazy
    private List<EmailSent> emailSentList;
}

Imagine now that you have a iteration with the person.getOrderList() and for every Order orderyou will do a order.getEmailSentList(). Can you see the problem now?

For LazyInitializationException you can have some solutions:

  • Use the OpenInSessionInView approach. You will need to create a WebFilter that will open and close the transaction. The problem with is the N+1 effect.
  • Use the hibernate.enable_lazy_load_no_trans configuration, that is a hibernate and you will not be able to port your project to other JPA provider if needed. You also can have the N+1 effect.
  • Use the EJB feature named PersistenceContext Extended. With this you will keep the context opened of several transactions. The problems are: N+1 effect can happen, use a lot of server memory (entities will stay managed)
  • Use the FETCH in the query. With this approach you could do a JPQL/HQL like: select p from Person p join fetch p.orderList. With this query you will have your list loaded from the database and will not have the N+1 effect. The problem is that you will need to write a JPQL for each case.

If you still have any problem, check these links:

Draken
  • 3,049
  • 13
  • 32
  • 49
uaiHebert
  • 1,784
  • 9
  • 20
  • OpenSessionInView will never have this "N+1 effect", that would'nt make any sense. Only _ONE_ session will be opened and every entity-lookup within that will be handled normally - most likely via JOINs, depending on your fields. You have obviously not understood how the open-session-in-view method works. The one and only problem with it is : in bad situations (and with bad coding) your user may not get any information if your transaction actually got rolled back or even failed, dending on your transaction-configuration. – specializt Oct 14 '14 at 11:20
  • 4
    N+1 is not about several sessions, but is about several trips do the database. – uaiHebert Oct 14 '14 at 14:36
  • in this case these are the same thing - during a view-created session all of your entity attributes may or may not be fetched with a single "trip" - depending on your ORM-implementation and/or fetch configuration. – specializt Oct 15 '14 at 12:12
  • 1
    I am not sure if you understood what I told up there. With open session in view you will have the N+1 effect, you will keep the transaction opened and for each get of a non fetched entity a new trip to the database will be done. I said about n+1 and fetch in a jpql, what is your point? – uaiHebert Oct 15 '14 at 18:39
  • 1
    ohgod ... that engrish is hard to decrypt, anyway : yes, you now discovered the primary aspect of lazy initialization. I recommend going deeper into the topic; such as the difference to eager-fetch, cardinalities and especially the fact that your "problem" is omnipresent and a JOIN FETCH will simply remove the DBMS-driver overhead for a few additional "trips" but also decrease the performance in large data-sets. JOINs may work for small data-sets exclusively. Hybrid approaches may also yield acceptable results. – specializt Oct 15 '14 at 19:39
  • 1
    I like this conversation, but both sides are so confident and conflicting. Can someone provide a reliable resource that illustrates the tradeoffs between lazy and eager? Also, let's address the fact that it's silly I can even choose @Lazy when there's another property I just "need to set" otherwise it doesn't work. – James Watkins May 21 '15 at 01:08
  • 1
    @uaiHebert , so do you recommend Use the FETCH in the query as best approach? – swapyonubuntu Aug 12 '15 at 10:42
  • Great answer. Would really appreciate an example though. I have tried a join query with @Basic(fetch=FetchType.EAGER) along with @Transactional(propagation=Propagation.Requires_new), (among hundreds of other attempts) but so far only the property has worked for me. The property works like a charm, but I’d rather not compromise my dozens of other entity classes in exchange. – Christian Meyer May 06 '19 at 22:12
11

This goes against how we can take advantage of Hibernate's enforcement of repeatable read semantics with the Session concept. When an object is first loaded and if the object is referenced again within the life of the session, then the same object is returned IRRESPECTIVE of whether this object has changed in the DB. This is the repeatable read semantics provided automatically by hibernate.

With this setting, you have no session providing this guarantee, so if you now access this data you will be getting the latest version of the data.

This might be fine. But consider the scenario where this object is held in some place for a long time and the data has changed considerably, so that the lazily fetched data is much different that the data already loaded when the session was alive. This is what you need to be concerned about.

To put it simple you can safely use this setting if your program is not affected by: How stale the data that was already fetched when in session to the data that will be fetched lazily out of session

But if this (your program is exposed to timing issues, when it might work fine one time and fail another time) is a concern, then fetch all the necessary data while in session.

codedabbler
  • 1,183
  • 6
  • 12
5

The best way to solve the LazyInitializationException is to use the JOIN FETCH directive in your entity queries.

EAGER loading is bad for performance. Also, there are anti-patterns such as:

Which you should never use since they either require the database connection to be open for the UI rendering (Open Session in View), or a database connection is needed for every lazy association that is fetched outside of the initial Persistence Context (hibernate.enable_lazy_load_no_trans).

Sometimes, you don't even need entities, and a DTO projection is even better.

Vlad Mihalcea
  • 103,297
  • 39
  • 432
  • 788
  • please explain the last sentence "don't even need entities, and a DTO projection is even better.". What is a DTO projection? and why it can replace entities. – Tiina Feb 23 '17 at 02:56
  • and what do you mean by "anti-patterns"? – Tiina Feb 23 '17 at 02:57
  • Anti-Pattern means that it's disguised as a solution while, in reality, it's a poor solution to a problem. – Vlad Mihalcea Feb 23 '17 at 05:13
  • I read your blog (the reference in your post) and also tried, if Post object in PostComment is empty, then the result would be wrong: when postComment exists, but it does not have Post, it should return a PostComment with empty/null Post, but it actually returns no PostComment. – Tiina Feb 23 '17 at 08:38
  • What to do if you use spring boot... like findById method? – robert trudel Oct 18 '17 at 19:29
  • If you use Spring Boot, it means you use Spring Data, so you can use a custom Repository method to execute the proper JPQL – Vlad Mihalcea Oct 18 '17 at 19:50
  • spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true is used in springboot – ASharma7 Jul 23 '19 at 10:13
  • By default, it's not used. If you enabled it explicitly, then you should consider removing this antipattern. – Vlad Mihalcea Jul 23 '19 at 10:15
  • @VladMihalcea as your comments and articles?Should we, dont use hibernate assocations(onetomany,manytoone..etc) for eager/lazy loading .Should we use inner joins with custom dtos,And we doesnt prefer lazy and eager loading both for performance issue?Should we? – Bilgehan May 08 '20 at 10:41
  • @VladMihalcea you have a lot articles and books and they are are very valuable for us,thanks for your sharing.For that question if it is possible just quick comment yes or no about hibernate assocations,for your DTO article ,as I understand we shouldnt use lazy loading?Y/N. – Bilgehan May 08 '20 at 11:38
2

Probably because there are better solutions, like @Transactional, where opening and closing sessions follows a very common pattern of "open a session then wrap everything in a try-catch-finally; catch rolls back and finally closes the session." This annotation is typically at the request-level for web apps and services.

Or if you need more granular control you can open sessions manually using a SessionFactory.

And as others have mentioned, lazy-loading is something you need to be aware of. It's not a silver bullet but it can be very helpful. Generally, if your apps are designed to have many small requests then its ok.

Eager loading can also be very bad. For example, when your object model has lots of many-to-many relationships but your requests don't use data more than one level deep.

Or you can just forget the whole thing for now. Use lazy loading until it becomes an issue. And if it does, you would have been better of with Mybatis anyway.

James Watkins
  • 4,252
  • 5
  • 26
  • 37