EntityManager 的 find() 方法是不是会创建 JPA 类的新实例?

Posted

技术标签:

【中文标题】EntityManager 的 find() 方法是不是会创建 JPA 类的新实例?【英文标题】:Does EntityManager's find() method create new instance of JPA class?EntityManager 的 find() 方法是否会创建 JPA 类的新实例? 【发布时间】:2011-08-23 15:49:25 【问题描述】:

我有点困惑。问题在标题中,这就是我要问的原因。 我在单个 VM 上运行 JSF + JPA Web 应用程序。 JPA 类有 @Transient 字段。现在想象一些网络用户打开一些页面并执行下面的代码

import javax.persistence.EntityManager;
// method 1 in backing bean
Agent a = entityManager.find(Agent.class, "007");
a.setTransientValue("Aston Martin");

当另一个网络用户/线程尝试读取该瞬态值时,我应该期待什么输出:

// method 2 in backing bean
Agent a = entityManager.find(Agent.class, "007");
String val = a.getTransientValue();

换句话说,就 JVM 而言,find() 方法是否总是返回新的类实例或相同或“取决于”?我已经通过 JSR-220 寻找答案,但没有成功,任何帮助或文档参考将不胜感激。

【问题讨论】:

【参考方案1】:

如果您在同一会话内(即在同一实体管理器生命周期内)调用find(..),则将返回相同的对象引用。 documentation of find() 指定了这一点:

如果实体实例包含在持久化上下文中,则从那里返回。

换句话说,EntityManager 包含一个实体集合(最有可能是映射)。当您调用 find 时,它会检查该集合。如果在那里找不到实体,则对数据库进行查询。返回的实体被放入地图中,因此后续调用会在那里找到它。

但请再次注意,这仅适用于一个会话的跨度。这通常与一个 http 请求相同(在 Web 应用程序上下文中)

【讨论】:

但它是由 JPA 规范保证还是只是实践? @Grzegorz Oledzki 看到更新。该方法的文档说明了这一点。 @Bozho,谢谢。不能说我已经彻底查看了正确的文档,但是任何链接/证明都会很有用。 @Osw 我贴了一个引用和方法文档的链接:) @Bozho thnks,希望这些实体不会被这些 N 级实体缓存机制或任何特定的 EJB/JPA 实现重用:)【参考方案2】:

要真正了解其工作原理,了解实体管理器与上下文之间的关系至关重要。

实体管理器是您访问实体的公共接口,但是,您的实体驻留在上下文中,附加到您的实体管理器。了解不同类型上下文的生命周期将回答您的问题。

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

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

事务范围

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

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

这也意味着,根据您设计交易的方式,您最终可能会得到多个上下文。

扩展持久性上下文

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

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

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

此外,在将 bean 标记为删除或您手动刷新它们之前,您的更改不会被刷新。

应用程序管理

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

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

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

根据您的应用程序,您可以决定创建一个全局实体管理器,其生命周期附加到应用程序本身的生命周期。也就是说,一个实体管理器用于应用程序的整个生命周期。在这种情况下,您的上下文将由您的实体管理器创建和销毁。

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

【讨论】:

以上是关于EntityManager 的 find() 方法是不是会创建 JPA 类的新实例?的主要内容,如果未能解决你的问题,请参考以下文章

Session.get() 和 EntityManager.find() Hibernate 有啥区别

EntityManager.find() 和 EntityManger.getReference() 有啥区别?

EntityManager.find(id) 会执行恶意攻击吗?

EntityManager.find 对象包含 2 个 id 作为主键的行为

JPA学习笔记(11)——使用二级缓存

播放框架:EntityManager 已关闭