52

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.

Vlad Mihalcea
  • 103,297
  • 39
  • 432
  • 788

9 Answers9

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")

Testo Testini
  • 2,120
  • 16
  • 27
andreak
  • 884
  • 7
  • 10
  • 2
    Could you add to your answer what this property does exactly? The implementation can have serious implications for data consistency or performance. Probably both. – iwein Aug 31 '12 at 08:43
  • 2
    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. – andreak Sep 07 '12 at 21:34
  • 6
    DO NOT USE THIS FEATURE. It is broken and you will loose your data. https://hibernate.atlassian.net/browse/HHH-7971 – Vojtěch Mar 17 '14 at 14:01
  • 3
    That bug was fixed in Hibernate version 4.3.5. This feature is safe to use starting with that Hibernate version. – Christopher Parker Dec 09 '14 at 04:33
  • 1
    At the time of writing there is one issue still open here: https://hibernate.atlassian.net/browse/HHH-8782. – Steve Chambers Jul 01 '15 at 08:51
  • 1
    I've just found this article, that discourages to use this parameter: https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/ – olivmir Feb 21 '18 at 09:05
  • 1
    Look at this article: https://vladmihalcea.com/the-best-way-to-handle-the-lazyinitializationexception/ – olivmir Feb 21 '18 at 09:15
  • 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: https://vladmihalcea.com/the-hibernate-enable_lazy_load_no_trans-anti-pattern/ – Christopher Will Feb 14 '19 at 12:13
17

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

iwein
  • 24,288
  • 9
  • 67
  • 108
jb.
  • 20,419
  • 16
  • 91
  • 130
  • 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:https://hibernate.atlassian.net/browse/HHH-8782. – Steve Chambers Jul 01 '15 at 08:49
  • 3
    "Or stop using Hibernate..." ... amen. – Night Owl Mar 29 '16 at 03:47
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

biegleux
  • 12,934
  • 11
  • 42
  • 52
Hani
  • 276
  • 3
  • 4
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.

Daniel Alexiuc
  • 12,568
  • 9
  • 54
  • 73
5

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.

Vlad Mihalcea
  • 103,297
  • 39
  • 432
  • 788
  • How is JOIN FETCH different from EAGER loading? Aren't both eager? – Eugen Labun Mar 14 '19 at 19:40
  • JOIN FETCH is explicitly set. EAGER fetching is implicit and not overrideable. – Vlad Mihalcea Mar 14 '19 at 19:43
  • 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? – Eugen Labun Mar 14 '19 at 19:45
  • 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. – Vlad Mihalcea Mar 14 '19 at 19:50
  • 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... – Eugen Labun Mar 14 '19 at 20:03
  • Eager loading is fine at query level, not mapping level or SessionFactory level. It's as simple as that. – Vlad Mihalcea Mar 14 '19 at 20:17
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.

cliff.meyers
  • 17,342
  • 5
  • 48
  • 66
  • Don't use open session in view if you expect load on your server. This answer could use a warning in that direction. – iwein Aug 31 '12 at 08:46
  • 1
    I thought the link would be sufficient to explain the implications of the pattern but I've added some warnings just in case. :) – cliff.meyers Oct 05 '12 at 01:57
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.

Shawn Chin
  • 74,316
  • 17
  • 152
  • 184
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.

K.Nicholas
  • 8,797
  • 4
  • 34
  • 53
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