Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
228 views
Welcome To Ask or Share your Answers For Others

1 Answer

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:

  1. If not using Spring why are you using @Transactional (by default even doesn't apply to HttpServlet)
  2. It had worked for WebLogic probably using some kind of tailored made solution

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...