JPA实体管理器应该被关闭吗?

90

我有下面的方法。

public Profile readUser(String email){
    EntityManager em = EMF.get().createEntityManager();
    return em.find(Profile.class, email);
}

上面的实体管理器使用是否正确?还是必须关闭em?有什么建议吗?


可能是Do I have to close() every EntityManager?的重复问题。 - rds
1
不行,绝对不行。除非你想要泄漏…… - user8558216
1个回答

150

我想答案是:这取决于情况

您的实体管理器是访问实体所在上下文的关键。如果您的应用程序是JSE应用程序,您必须考虑上下文的生命周期。

假设您将为每个用户请求创建一个实体管理器。因此,在处理某个请求时,您将保持实体管理器处于打开状态,并在完成后关闭它。

在JSE应用程序中,您可能已经考虑过要在整个应用程序生命周期内保持实体管理器处于打开状态(假设您没有处理大量数据),然后在应用程序关闭时关闭它。

总之,何时打开和关闭完全取决于您的策略和设计。当您不再需要其上下文中的实体时,就可以将其关闭。

在您的示例中,这并不明显,但由于您在方法中创建EM,因此应在返回之前将其关闭,否则您将无法再次访问它(除非您将其保留在某个注册表中,在代码中并不明显)。

如果您不关闭它,则即使在使用完毕后,您的实体也将保持为已附加状态。即使您无法再访问EM,您的上下文仍将保持活动状态。

JPA规范包含更多详细信息。在7.7应用程序管理的持久性上下文一节中,它说:

当使用应用程序管理的实体管理器时,应用程序直接与持久性提供者的实体管理器工厂交互以管理实体管理器的生命周期,并获得和销毁持久化上下文。所有这些应用程序管理的持久化上下文都是扩展范围的,可以跨越多个事务。使用EntityManagerFactory.createEntityManager方法以及EntityManager closeisOpen方法来管理应用程序管理的实体管理器及其关联的持久化上下文的生命周期。扩展的持久化上下文存在于使用EntityManagerFactory.createEntityManager创建实体管理器的点,直到通过EntityManager.close关闭实体管理器为止。从应用程序管理的实体管理器获取的扩展的持久化上下文是独立的持久化上下文,不随事务传播。EntityManager.close方法关闭实体管理器以释放其持久化上下文和其他资源。调用close后,应用程序除了getTransactionisOpen方法外,不能再调用EntityManager实例上的任何其他方法,否则将引发IllegalStateException。如果在事务处于活动状态时调用close方法,则持久化上下文仍然受管理,直到事务完成。EntityManager.isOpen方法指示实体管理器是否打开。直到实体管理器被关闭,isOpen方法返回true。因此,实体管理器是访问实体的公共接口,但是实体驻留在上下文中,与实体管理器连接。理解不同类型上下文的生命周期将回答您的问题。持久化上下文可以有不同类型。在Java EE应用程序中,可以使用事务范围的持久化上下文或扩展持久化上下文。在JSE应用程序中,上下文的性质由开发人员控制。当您向实体管理器请求实体时,它会查找其附加的上下文中的实体,如果在那里找到实体,则返回该实体,否则,它将从数据库检索实体。之后在上下文中对此实体的调用将返回相同的实体。

在使用事务范围的持久性上下文的Java EE应用程序中,当您首次访问实体管理器时,它会检查当前JTA事务是否已附加上下文。如果尚未存在上下文,则会创建一个新上下文,并将实体管理器链接到该上下文。然后从数据库中读取实体(如果存在缓存,则从缓存中读取),并将其放置到上下文中。当事务结束(提交或回滚)时,上下文变得无效,并且其中的任何实体都变为分离状态。这是无状态会话Bean的经典场景。

@PersistenceContext(unitName="EmplService")
EntityManager em;

这也意味着,根据您设计事务的方式,可能会有多个上下文。

扩展持久化上下文

在具有有状态会话bean的Java EE应用程序中,您可能希望上下文在多个bean调用之间继续存在,因为您不想在标记删除之前提交,对吧?在这种情况下,您需要使用扩展持久化上下文。在这种情况下,持久化上下文是在首次需要时创建的,但直到您标记有状态bean以进行删除之前它才会失效。

@PersistenceContext(unitName="EmplService", type=PersistenceContextType.EXTENDED)

这意味着,无论将哪个实例的实体管理器注入到此bean中,在后续调用有状态会话bean方法时,您都可以确保始终访问相同的上下文,因此即使是后续调用也会返回相同的实例,因为它是相同的上下文。

此外,除非将bean标记为删除或手动刷新更改,否则更改不会被提交。

应用程序管理

您始终可以手动实例化您的实体管理器工厂和实体管理器。这是您通常在JSE应用程序中要做的,对吗?

对于此类应用程序,您通常没有容器来处理JTA事务,对吧?因此,您使用资源本地事务,并负责手动提交或回滚更改。

对于此类应用程序,在实例化实体管理器时,会自动附加上下文。

根据您的应用程序,您可以决定创建一个全局实体管理器,其生命周期与应用程序本身的生命周期相关联。也就是说,整个应用程序的单个实体管理器。在这种情况下,您的上下文将随着实体管理器的创建和销毁而创建和销毁。

或者,您可以为与应用程序用户的每个交谈(即事务)创建一个实体管理器。在这种情况下,作用域由您确定,但仍然会随着实体管理器的创建和销毁而创建和销毁上下文。


很好的回答,但我需要知道:在一个会话期间多次打开和关闭EntityManager是否会有高性能成本?是只实例化和关闭一次,还是在每个数据库CRUD操作中实例化/使用/关闭它,哪种方法最好?“这取决于”好吧,但必须有适用于大多数用例的更合适的用法。 - tomrlh
7
就我而言,创建 EntityManager 的成本应该可以忽略不计。在我看来,EntityManager 只是一个用于处理当前事务的 工作单元 的抽象。我认为每个事务创建和销毁一个 EntityManager 是完全可以的。但是,EntityManager 也有其他影响,因为它作为实体的事务缓存,因此具有良好定义的事务范围并正确处理实体可能会利用这个缓存。 - Edwin Dalorzo
EntityManager.close方法关闭实体管理器以释放其持久化上下文。什么是持久化上下文? - gstackoverflow
这也意味着,根据您设计事务的方式,您可能会得到多个上下文。您能否解释一下? - gstackoverflow

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接