Java JPA 延迟加载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21647947/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 09:51:22  来源:igfitidea点击:

JPA Lazy Loading

javahibernatejakarta-eejpa

提问by Leos Literak

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.

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

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:

该应用程序是 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();

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

我使用了与 WebLogic/JPA1 非常相似的模式,它运行流畅。任何的想法?谢谢

PS. this is a JPA application, I do not have hibernate session etc.

附注。这是一个 JPA 应用程序,我没有休眠会话等。

采纳答案by Ori Dar

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:

在您的 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();

This should do, plus you have a single transaction and less JDBC calls.

这应该可以,而且您只有一个事务和更少的 JDBC 调用。

This is helpful for that specific use case where it that specific Servlet method call.

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

pre-fetch collection in EJB

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 OAuthLogincollection with the Userwhile preserving a FetchType.LAZYand avoid LazyInitializationExceptionfor that specific collection only.

这对于您想要获取OAuthLogin集合的每种情况都有帮助,User同时保留 aFetchType.LAZY并避免LazyInitializationException仅用于该特定集合。

Use Open Entity Manager in View Filter

在视图过滤器中使用 Open Entity Manager

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

这将基本上防止LazyInitializationException,每个延迟获取的关联,每个跨范围的实体应用程序

PS:

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
  1. 如果不使用 Spring 你为什么使用@Transactional(默认情况下甚至不适用于HttpServlet
  2. 它可能使用某种量身定制的解决方案为 WebLogic 工作

回答by Andreas Aumayr

LazyInitializationException means that you access a lazy collection AFTER the associated session had been closed.

LazyInitializationException 意味着您在关联会话关闭后访问惰性集合。

As your code looks fine, your problem may be the same like in this question LazyInitializationException in JPA and Hibernate

由于您的代码看起来不错,您的问题可能与此问题中的LazyInitializationException in JPA 和 Hibernate 相同

Can you verify that your transaction is opened in the beginning of the method?

你能验证你的交易是在方法开始的时候打开的吗?

回答by GuapoSasa

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 infohttp://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving#Use_the_Maven_plugin

在JPA中初始化laze,需要调用一个jar库并启动它,如果是用maven或者manual por example,如果需要
laze ,在maven jar中使用jar de.empulse.eclipselink 更多信息http://wiki. eclipse.org/EclipseLink/UserGuide/JPA/Advanced_JPA_Development/Performance/Weaving/Static_Weaving#Use_the_Maven_plugin