Four solutions to the LazyInitializationException – Part 2
Load collection by Stateful EJB with PersistenceContextType.EXTENDED
This approach can be applied only to applications that works with Full JEE environments: to use a EJB with PersistenceContextType.EXTENDED.
Check the code below how the DAO would look like:
package com.ejb; import javax.ejb.Stateful; import javax.persistence.*; import com.model.Person; @Stateful public class SystemDAOStateful { @PersistenceContext(unitName = 'LazyPU', type=PersistenceContextType.EXTENDED) private EntityManager entityManager; public Person findByName(String name) { Query query = entityManager.createQuery('select p from Person p where name = :name'); query.setParameter('name', name); Person result = null; try { result = (Person) query.getSingleResult(); } catch (NoResultException e) { // no result found } return result; } }
public class DataMB { // other methods and attributes @EJB private SystemDAOStateful daoStateful; public Person getPersonByStatefulEJB() { return daoStateful.findByName('Mark M.'); } }
<h:dataTable var='dog' value='#{dataMB.personByStatefulEJB.lazyDogs}'> <h:column> <f:facet name='header'> Dog name </f:facet> #{dog.name} </h:column> </h:dataTable>
Pros and Cons of this approach:
Pros | Cons |
The container will control the database transaction | Works only for JEE |
The model classes will not need to be edited | N+1 effect may happen |
A great amount of Stateful EJBs may affect the container memory. |
This approach may create the N+1 effect and the Stateful EJB has a characteristic of not being removed/destroyed while the its session is not expired or until it lost its reference.
Warning: It is not a good practice to hold a reference to an injected EJB in objects that remain in a Pool. The JSF will create a ManagedBean pool to handle better the user requests; when it is necessary the container will increase or decrease the number of ManagedBeans in the pool. In the code of this post imagine if the container create 100 instances of ManagedBeans in the pool, the server will hold 100 Stateful EJBs in memory. The solution to this problem would be a JNDI LookUp to the Stateful EJB.
Load collection by Join Query
This solution is easy to understand and to apply.
See the code below:
public Person findByNameWithJoinFech(String name) { Query query = entityManager.createQuery('select p from Person p join fetch p.lazyDogs where p.name = :name'); query.setParameter('name', name); Person result = null; try { result = (Person) query.getSingleResult(); } catch (NoResultException e) { // no result found } return result; }
public Person getPersonByQuery() { return systemDAO.findByNameWithJoinFech('Mark M.'); }
<h:dataTable var='dog' value='#{dataMB.personByQuery.lazyDogs}'> <h:column> <f:facet name='header'> Dog name </f:facet> #{dog.name} </h:column> </h:dataTable>
Pros and Cons of this approach:
Pros | Cons |
Just one query will be fired in the database | It would be necessary one query for each accessed collection/lazy attribute |
The model classes will not need to be edited | |
Will bring only the desired data | |
The N+1 effect will not happen |
This approach has the disadvantage of the need of a new query to access each model class collection/lazy attribute. If we need to query only the Person dogs we would need of a specific query. Imagine that we would need to query for the Person emails, it would be necessary a different query.
This approach can be applied to JSE and JEE.
EclipseLink and lazy collection initialization
The relationships default values are:
Relationship | Fetch |
@OneToOne | EAGER |
@OneToMany | LAZY |
@ManyToOne | EAGER |
@ManyToMany | LAZY |
But the JPA Spec* says that:
The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. In particular, lazy fetching might only be available for Basic mappings for which property-based access is used.
As you can see in the text above, the JPA implementation may ignore the hint strategy if it wants to. The EclipseLink has a behavior with JEE and other behavior to JSE. You can see each behavior here: http://wiki.eclipse.org/Using_EclipseLink_JPA_Extensions_%28ELUG%29#What_You_May_Need_to_Know_About_EclipseLink_JPA_Lazy_Loading
We can find in the internet some people saying that even with a lazy collection the EclipseLink does the n+1 queries when the entity is loaded. And we can find this behavior of users with Glassfish and EJB.
Below you will see some tips to use correctly lazy load with EclipseLink:
- http://stackoverflow.com/questions/8490532/eclipselink-lazy-loading
- http://stackoverflow.com/questions/3932623/eclipselink-dont-fetch-some-fields-by-default
- https://forums.oracle.com/forums/thread.jspa?messageID=1706796
* JSR-000220 Enterprise JavaBeans 3.0 Final Release (persistence) 9.1.18 and will repeat to the otters JPA relationships.
The end!
In my opinion the best solution is the Join Fetch Query. It is up to you to choose the best solution to your application.
Click here to download the source code of this post. If you want to run the code of this post you will need to create a database named LazyExceptionDB and the JBoss module. Attached to the source code is the Postgres module. If you want to see how to set up the datasource and the Postgres or MySQL module you can see it here: Full WebApplication JSF EJB JPA JAAS.
I hope this post might help you.
If you have any comment or doubt just post it.
See you soon.
Reference: Four solutions to the LazyInitializationException from our JCG partner Hebert Coelho at the uaiHebert blog.
Clap clap for you mate. Nice and easy.
Nice tips… Congratulations!
Excellent and very useful note!, Thanks!!!!
Just a few quick notes on how to design entities and/or your code to handle the infamous LIE. One other way to handle LIE is to essentially wrap those areas of your code (based on testing) to catch LIE and return something else that say “this collection you’re trying to access may have data, but I don’t have it right now.” You can design a lazy loading pattern to queue this request as a Promise and pickup the results at a later time. Yes, the “join fetch” and “left join fetch” are your best options but you’ll have to be… Read more »
Your article is really good, it helped a lot! I follow your fourth approach (load collection by fetch query) but it throws: “..SQLGrammar exception “You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘fetch e.almacenesFree where e.mail = ‘test’ ” public Estandar buscarPorMailConAlmacenesGratis(String mail) { Estandar ret = null; Query query = em .createNativeQuery(“select e from estandar e left join fetch e.almacenesFree where e.mail = :email”); query.setParameter(“email”, mail); try { ret = (Estandar) query.getSingleResult(); } catch (NoResultException e) { } return ret; } And… Read more »
When you use “createNativeQuery” then you must use regular SQL, not JPQL. So replace the actual query with this:
Estandar ret = null;
try {
String sql = “select * from estandar e left join almacenesFree af on e.almacenesFree_id = af.id and e.mail=”+mail;
ret = (Estandar) em.createNativeQuery(sql, Estandar.class)
.setParameter(“email”, mail)
.query.getSingleResult();
} catch (…) {
} finally {
}
i haven’t tried the code above, so beware.
Thx
-Rob
excellent explanation mate, this make me analize clearly my application and decide which solution to use. I think i’m going with fetchtype EAGER, cause my database only consist in catalog tables and application behavior (i.e. menus and metadata properties). So in this case I don’t think my application would have a 400,000,000 menu options for example.