JPA Lazy Loading
Solution 1
There are few alternatives you can use:
Use cascading persistence:
@OneToMany(fetch=FetchType.LAZY, mappedBy="user", cascade = {CascadeType.PERSIST})
List<OAuthLogin> oauthLogins;
In your Servlet do:
User user = new User(name);
user.setEmail(email);
OAuthLogin fbLogin = new OAuthLogin(user, OAuthProvider.FACEBOOK, "1501791394");
user.getOauthLogins().add(fbLogin) // this is enough assuming uni-directional association
userDAO.persist(user);
List<OAuthLogin> oauthLogins = user.getOauthLogins();
This should do, plus you have a single transaction and less JDBC calls.
This is helpful for that specific use case where it that specific Servlet method call.
pre-fetch collection in EJB
public User findById(int id, boolean prefetch) {
User entity = em.find(User.class, id);
if (prefetch) {
// Will trigger 1 or size JDBC calls depending on your fetching strategy
entity.getOauthLogins().size()
}
return entity;
}
Alternatively, Override fetch mode using a criteria
This is helpful for every case you want to fetch OAuthLogin
collection with the User
while preserving a FetchType.LAZY
and avoid LazyInitializationException
for that specific collection only.
Use Open Entity Manager in View Filter
Just Google it, you'll find plenty of examples
This will basically prevents LazyInitializationException
, per every association fetched lazily, per each Entity application cross-wide
PS:
- If not using Spring why are you using
@Transactional
(by default even doesn't apply toHttpServlet
) - It had worked for WebLogic probably using some kind of tailored made solution
Solution 2
LazyInitializationException means that you access a lazy collection AFTER the associated session had been closed.
As your code looks fine, your problem may be the same like in this question LazyInitializationException in JPA and Hibernate
Can you verify that your transaction is opened in the beginning of the method?
Solution 3
To initialize laze in JPA, you need to invoke a jar library and start it, if it is with maven or manual
por example , if you need laze, use jar in maven
jar de.empulse.eclipselink
More info
http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving#Use_the_Maven_plugin
Leos Literak
Java enthusiast (since 1996), author of www.abclinuxu.cz website (aka stackexchange for czech/slovak linux users), solution architect, developer ..
Updated on May 10, 2020Comments
-
Leos Literak about 4 years
I have a problem with lazy loading property in JPA entity. I read many similar questions, but they are related to spring or hibernate and their answears are either not applicable or helpful.
The application is JEE with JPA2.1 running on Wildfly application server. There are two entities, DAO session bean and servlet that puts it together:
@Entity @Table(name = "base_user") public class User implements Serializable { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id") int id; @OneToMany(fetch=FetchType.LAZY, mappedBy="user") List<OAuthLogin> oauthLogins; } @Entity @Table(name = "oauth_login") public class OAuthLogin implements Serializable { @ManyToOne @JoinColumn(name="user_id", nullable=false) User user; } @Stateless(name = "UserDAOEJB") public class UserDAO { @PersistenceContext(unitName="OAUTHDEMO") EntityManager em; public User findById(int id) { User entity; entity = em.find(User.class, id); return entity; } } public class SaveUserServlet extends HttpServlet { @EJB UserDAO userDAO; @Transactional protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { User user = new User(name); user.setEmail(email); System.out.println("Persisting user " + user); userDAO.persist(user); OAuthLogin fbLogin1 = new OAuthLogin(user, OAuthProvider.FACEBOOK, "1501791394"); loginDAO.persist(fbLogin1); User user2 = userDAO.findById(user.getId()); List<OAuthLogin> oauthLogins = user2.getOauthLogins();
When I run this code, it fails with:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: cz.literak.demo.oauth.model.entity.User.oauthLogins, could not initialize proxy - no Session org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:572) org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:212) org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:551) org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:140) org.hibernate.collection.internal.PersistentBag.iterator(PersistentBag.java:294) cz.literak.demo.oauth.servlets.SaveUserServlet.doPost(SaveUserServlet.java:66)
I used very similar pattern with WebLogic/JPA1 and it ran smoothly. Any idea? Thanks
PS. this is a JPA application, I do not have hibernate session etc.
-
Leos Literak over 10 yearsThanks, I will try it in a moment. ad 1) it is javax.transaction.Transactional from JPA 1.2. I tried to explicitely specify it, but it did not help.
-
Ori Dar over 10 yearsThanks for the update. BTW the annotation is part of the JTA specification and applies to CDI managed beans
-
Leos Literak over 10 yearsI tried the first approach, the user was persisted correctly. Then I searched for this entity and get the list, but the same exception occured again> User user2 = userDAO.findById(user.getId()); > oauthLogins = user2.getOauthLogins();
-
Ori Dar over 10 yearsThis is true, it only good for the case where you saves the parent entity, and iterate the children in the same "stroke". However, you don't have go to the database again and fetch
user2
while you haveuser
at hand. These two objects represent the same database row (and theUser
equals method should return true foruser.equsls(user2)
) -
Leos Literak over 10 yearsThe second approach works! Could you please explain me, why method invocation in DAO is different than in servlet? Why it works in findById and throws LazyInitializationException in servlet without this workaround?
-
Ori Dar over 10 yearsBecause in the DAO, you are within a transaction method. Every EJB method is implicitly transactional
-
Leos Literak over 10 years
-
Leos Literak over 10 yearsIf I call DAO from EJB, is it neccessary to use prefetch? The transaction shall be the same in both EJB call and DAO method (unless requires new transaction mode is used).
-
Ori Dar over 10 yearsIf you call your DAO method from another EJB business method, then you are already withing transaction and the Persistence Context of both EJBs is same, so you don't have to set prefetch to true, or any. EJB business methods are implicitly called with
TransactionAttributeType(REQUIRED)
, so both in that case participate in the same transaction by default -
Leos Literak over 10 years"@javax.transaction.Transactional annotation enables an application to declaratively control transaction boundaries on CDI-managed beans, as well as classes defined as managed beans, such as servlets .." so I expected that doPost will have the transaction as well
-
Leos Literak over 10 yearsI surrounded servlet code with UserTransaction and there is no LazyLoadingExc even without explicit prefetch. :-)