JPA实体管理器是否应该关闭?


82

我有下面的方法。

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

实体管理器的上述用法可以吗?或者有必要关闭em吗?有任何建议。



不就是不。除非您想泄漏...

Answers:


131

我想答案是:取决于

您的实体管理员是获得对实体所在上下文访问权限的关键。如果您的应用程序是JSE应用程序,则必须考虑上下文的​​预期寿命是多少。

让我们考虑您将根据用户的请求创建一个实体管理器。因此,在您处理给定的请求时,您将保持实体管理器处于打开状态,完成后将其关闭。

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

底线,打开和关闭的时间完全取决于您的策略和设计。当您不再需要其上下文中的实体时,可以关闭它。

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

如果不关闭它,即使使用完实体,您的实体也将保持连接状态。即使您无法再访问EM,您的上下文也将保持活动状态。

JPA规范包含了更多的细节。在第7.7节“应用程序管理的持久性上下文”中,它说:

使用由应用程序管理的实体管理器时,应用程序直接与持久性提供程序的实体管理器工厂进行交互,以管理实体管理器的生命周期并获取和销毁持久性上下文。

所有这些应用程序管理的持久性上下文都在范围上进行了扩展,并且可以跨越多个事务。

EntityManagerFactory.createEntityManager方法以及 EntityManager closeisOpen方法用于管理应用程序管理的实体管理器及其关联的持久性上下文的生命周期。

从使用创建实体管理器的那一刻开始存在扩展的持久性上下文, EntityManagerFactory.createEntityManager直到通过关闭实体管理器为止EntityManager.close

从应用程序管理的实体管理器获得的扩展持久性上下文是一个独立的持久性上下文,它不随事务传播。

[...]该EntityManager.close方法关闭实体管理器以释放其持久性上下文和其他资源。调用close之后,应用程序不得在EntityManager实例上调用除getTransaction和以外的任何其他方法 isOpen,否则IllegalStateException将引发。如果在事务处于活动状态时调用close方法,则持久性上下文将保持受管理状态,直到事务完成为止。

EntityManager.isOpen方法指示实体管理器是否打开。isOpen在关闭实体管理器之前,该方法返回true。要真正了解其工作原理,了解实体管理器与上下文之间的关系至关重要。

因此,如您所见,实体管理器是用于访问实体的公共接口,但是,实体位于附加到实体管理器的上下文中。了解不同类型的上下文的生命周期将回答您的问题。

持久性上下文可以具有不同的类型。在Java EE应用程序中,可以具有事务范围的持久性上下文扩展的持久性上下文。在JSE应用程序中,上下文的性质由开发人员控制

当您向实体管理器请求实体时,它会在其附加上下文中查找该实体,如果在该实体中找到该实体,则将其返回,否则,它将从数据库中检索该实体。在上下文中对该实体的后续调用将返回相同的实体。

交易范围

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

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

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

扩展持久性上下文

在带有状态会话Bean的Java EE应用程序中,您可能希望上下文在多个Bean调用中都可以生存,因为您不希望在将Bean标记为要删除之前提交,对吗?在这些情况下,您需要使用扩展的持久性上下文。在这种情况下,持久性上下文是在第一次需要它时创建的,但是直到您标记要删除的有状态Bean时,它才会变得无效。

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

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

另外,在标记为要删除该bean或手动刷新它们之前,不会清除您的更改。

应用管理

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

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

对于这种应用程序,当您实例化实体管理器时,上下文会自动附加到它。

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

或者,您可以根据与应用程序用户的每次对话(即交易)创建一个实体管理器。在这种情况下,范围由您确定,但是仍然可以使用实体管理器来创建和销毁您的上下文。


很好的答案,但是我需要知道:在会话期间多次打开和关闭EntityManager会带来很高的性能吗?实例化和关闭一次,或在每个数据库操作中实例化/使用/关闭它,哪种方法最好?“取决于”好,但必须适合大多数用例
。.– tomrlh

4
@tomurlh就我而言,创建的成本EntityManager必须忽略不计。在我看来,EntityManager只是处理当前事务工作单元的一种抽象。我相信每笔交易创建和销毁它是完全可以的。现在,它的确具有其他含义,因为EntityManager服务器作为实体的事务缓存,因此具有明确定义的事务范围并正确处理实体可能会利用此缓存。
Edwin Dalorzo '18年

EntityManager.close方法关闭实体管理器以释放其持久性上下文是什么持久性上下文?
gstackoverflow

这也意味着,根据您设计事务的方式,您可能会遇到多个上下文。 你能解释一下吗?
gstackoverflow
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.