How to solve the LazyInitializationException when using JPA and Hibernate
Asked Answered
L

9

54

I am working on a project for a customer who wants to use lazy initialization. They always get "lazy initialization exception" when mapping classes with the default lazy loading mode.

@JoinTable(name = "join_profilo_funzionalita", joinColumns = {@JoinColumn(name =    "profilo_id", referencedColumnName = "profilo_id")}, inverseJoinColumns = {@JoinColumn(name = "funzionalita_id", referencedColumnName = "funzionalita_id")})
//@ManyToMany(fetch=FetchType.EAGER) - no exceptions if uncommented
@ManyToMany 
private Collection<Funzionalita> funzionalitaIdCollection;

Is there a standard pattern using JPA classes to avoid this error?

Snippets are welcome, thanks a lot for your time.

Littlejohn answered 23/2, 2009 at 17:4 Comment(0)
S
61

Hibernate 4.1.6 finally solves this issue: https://hibernate.atlassian.net/browse/HHH-7457

You need to set the hibernate-property hibernate.enable_lazy_load_no_trans=true

Here's how to do it in Spring:

<bean id="entityManagerFactory"
      class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="packagesToScan" value="com.mycompany.somepackage"/>
    <property name="jpaVendorAdapter" ref="hibernateVendorAdapter"/>
    <property name="jpaDialect" ref="jpaDialect"/>
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.enable_lazy_load_no_trans">true</prop>
        </props>
    </property>
</bean>

Voila; Now you don't have to worry about LazyInitializationException while navigating your domain-model outside of a hibernate-session (persistence-context in "JPA-speak")

Synchro answered 11/8, 2012 at 9:10 Comment(8)
Could you add to your answer what this property does exactly? The implementation can have serious implications for data consistency or performance. Probably both.Weiland
This option loads the lazy-association outside the transaction, meaning that the data might not be consistent with what the parent-entity's. But that isn't any worse than using Spring's OpenEntityManagerInViewFilter, which also loads stuff outside a transaction. I'm using field-based mapping and haven't experienced JIT messing up the generated proxies.Synchro
DO NOT USE THIS FEATURE. It is broken and you will loose your data. hibernate.atlassian.net/browse/HHH-7971Ossify
That bug was fixed in Hibernate version 4.3.5. This feature is safe to use starting with that Hibernate version.Brahmi
At the time of writing there is one issue still open here: hibernate.atlassian.net/browse/HHH-8782.Ames
I've just found this article, that discourages to use this parameter: vladmihalcea.com/…Merciful
Look at this article: vladmihalcea.com/…Merciful
Please DO NOT USE this without knowing exactly what you are doing. Using enable_lazy_load_no_trans is an anti-pattern and can be unintentionally invasive and expensive in terms of performance and i/O. Please read at least following article first: vladmihalcea.com/…Misspell
L
18

There are many ways to pre-fetch properties, so they are there after session is closed:

  1. Call appropriate getter. After field is fetched into bean it is there after session is closed.
  2. You may initialize field in EJBQL query , look for JOIN FETCH keyword.
  3. Enable AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS if you're on a Hibernate version that supports it.

Several problems may occur when you try these solutions:

  1. The getters' invocation may be optimized away by the JIT compiler (sometimes this takes a while).
  2. The entities you are trying to JOIN FETCH may be linked through multiple 'many' relationships involving List's. In this case the resulting query returns ambiguous results and Hibernate will refuse to fetch your data in a single query.
  3. There is already one interesting bug related to AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS. And there will be more because as the hibernate guys say: Note: this may happen outside of the transaction and is not safe. Use with caution. You're on your own mostly.

The best way to go is to try a JOIN FETCH first. If that doesn't work try the getter approach. If that gets messed up at runtime by the JIT compiler, assign the result to a public static volatile Object.

Or stop using Hibernate...

Landa answered 10/5, 2009 at 11:45 Comment(3)
For point (3) worth noting the issue is linked bug is closed as of version 4.1.7. At the time of writing there is still a related open issue here:hibernate.atlassian.net/browse/HHH-8782.Ames
"Or stop using Hibernate..." ... amen.Provide
You may use JOIN FETCH only on a method that loads eagerly for this purpose you have in mind while all the other remaining methods continue lazy.Pronuba
I
16

Note that you shouldn't use hibernate.enable_lazy_load_no_trans pre Hibernate 4.1.7, as it leaks connections. See https://hibernate.onjira.com/browse/HHH-7524

Intrusive answered 6/9, 2012 at 21:20 Comment(1)
it works, but now I have recursive cycling problem with hibernate mappingFeriga
M
8

LazyInitializationException means that you are calling the collection after the hibernate session has closed, or after the object has been detached from the session.

You need to either re-attach the object to hibernate session, change the place where you are calling the collection, or move the boundary of where the session gets closed to a higher layer.

Monkhood answered 23/3, 2009 at 2:53 Comment(1)
Exactly, We got the WHAT part ; HOW is the question!Pome
B
7

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

FetchType.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. You should fetch entities only when you need to modify them. For read-only transactions, DTO projections are better.

Butterandeggs answered 27/9, 2016 at 11:13 Comment(6)
How is JOIN FETCH different from EAGER loading? Aren't both eager?Nally
JOIN FETCH is explicitly set. EAGER fetching is implicit and not overrideable.Butterandeggs
But both are essentially eager (explicitly or implicitly). Do I understand correctly that you are proposing to use eager fetching/loading as a solution for lazy loading?Nally
But only one can be LAZY by default. If you read the linked articles, you'll get a better understanding of what I wrote as a conclusion in this article.Butterandeggs
I read your article about hibernate.enable_lazy_load_no_trans (and many others, too; btw. I very appreciate your work) but still have troubles to understand why it's considered to be a bad practise. For example, you show a list of orders to a user. The user can click on an order to see details. It seems to me as a natural approach to use hibernate.enable_lazy_load_no_trans for such (very typical) cases. An eager loading/fetching of all details for all orders in advance feels very wrong...Nally
Eager loading is fine at query level, not mapping level or SessionFactory level. It's as simple as that.Butterandeggs
S
5

OpenSessionInView is one pattern to deal with this problem. Some info here:

http://www.hibernate.org/43.html

You'll want to be cautious when implementing this pattern and understand the implications. Each time you navigate a lazy association in the view it will fire off another SQL query to load the data. If your use cases are such that the number and size of these SQL queries is small then this may not matter. Make sure that at a minimum you adjust your logging settings so you can see what kind of queries Hibernate is "magically" executing in the background for you to load the data.

Also consider the kind of application you are writing. If you're not dealing with remoting (no web services, no AJAX-based web client) then OSIV may work very nicely. However, if a remoting serializer starts to walk the entire object graph, it will likely trigger a ridiculous number of SQL queries and cripple your DB and app server.

Scratchboard answered 23/2, 2009 at 18:19 Comment(2)
Don't use open session in view if you expect load on your server. This answer could use a warning in that direction.Weiland
I thought the link would be sufficient to explain the implications of the pattern but I've added some warnings just in case. :)Scratchboard
U
4

When you are using collection and you want to initialize it with lazy loading then use that collection before session close. If session is close after that if you want to use then you get lazyinitializeException because lazy is try by default.

Uttasta answered 2/11, 2011 at 12:39 Comment(0)
S
1

The Oracle Java tutorials point out that "Enterprise beans support transactions, the mechanisms that manage the concurrent access of shared objects." So, in order to handle the Lazy Fetch issues I create a Stateless Java Session Bean and then get all of the sub classes I need before returning from the method. This avoids the lazy fetch exception. Oracle has also referred to this as a "Session Façade" core J2EE pattern. This pattern seems better than some of the other practices mentioned.

Softwood answered 26/10, 2014 at 21:33 Comment(0)
W
0

I'm working on a project that aims to solve common JPA problems when mapping entities to DTOs using ModelMapper. This issue has already been solved on the project. Project link: JPA Model Mapper

"Its crucial for performance to declare entities as lazy load so we don't need to fetch all related entities every time we need some data. But this technique leads to some issues. The most common one is the LazyInitializationException that can be pretty annoying sometimes. Most of the time we would just want a null object for a not loaded entity instead of an object that throws an exception if accessed..."

Source: JPA Model Mapper

Therefore, in the project we deal with LazyInitializationException by setting null for all not loaded entities. The examples below show how it works.

Remapping an entity setting null for all not loaded entities:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemEntity.class);

Remapping an entity to a DTO setting null for all not loaded entities:

TypedQuery<SystemEntity> query =
        em.createQuery("select s from SystemEntity s where s.id = 1",  SystemEntity.class);

SystemEntity system = query.getSingleResult();
return new JpaModelMapper(em).mapEntity(system, SystemDTO.class);

For more informations please see JPA Model Mapper

Wiese answered 17/4, 2018 at 19:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.