JPA懒惰加载

时间:2014-02-08 15:37:16

标签: java hibernate java-ee jpa

我在JPA实体中遇到延迟加载属性的问题。我读了许多类似的问题,但它们与spring或hibernate相关,它们的问题不适用或者有用。

应用程序是JEE,在Wildfly应用程序服务器上运行JPA2.1。有两个实体,DAO会话bean和servlet将它们组合在一起:

@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();

当我运行此代码时,它失败了:

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)

我在WebLogic / JPA1中使用了非常相似的模式,运行顺畅。任何的想法?感谢

PS。这是一个JPA应用程序,我没有hibernate会话等。

3 个答案:

答案 0 :(得分:15)

您可以使用的替代品很少:

使用级联持久性:

@OneToMany(fetch=FetchType.LAZY, mappedBy="user", cascade = {CascadeType.PERSIST})
List<OAuthLogin> oauthLogins;

在你的Servlet中:

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();

这应该这样做,而且你有一个事务和更少的JDBC调用。

这对于特定Servlet方法调用的特定用例很有帮助。

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;
}

或者,Override fetch mode using a criteria

这对于您希望使用OAuthLogin获取User集合并同时保留FetchType.LAZY并且仅针对该特定集合避免LazyInitializationException的每个案例都很有用。

在View Filter中使用Open Entity Manager

只是谷歌,你会发现很多例子

对于跨越每个实体应用程序的每个懒惰地提取的关联,这基本上会阻止LazyInitializationException

PS:

  1. 如果不使用Spring,为什么使用@Transactional(默认情况下甚至不适用于HttpServlet
  2. 它可能适用于WebLogic,可能使用某种定制的解决方案

答案 1 :(得分:3)

LazyInitializationException表示您在关联的会话关闭后访问延迟收藏。

由于您的代码看起来很好,您的问题可能与此问题LazyInitializationException in JPA and Hibernate

中的问题相同

您可以验证您的交易是在方法开头打开的吗?

答案 2 :(得分:1)

要在JPA中初始化laze,你需要调用一个jar库并启动它,如果它是maven或manual 例如,如果你需要懒散,在maven中使用jar jar de.empulse.eclipselink  More info http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving#Use_the_Maven_plugin