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

Posted

技术标签:

【中文标题】EntityManager.find() 和 EntityManger.getReference() 有啥区别?【英文标题】:What is the difference between EntityManager.find() and EntityManger.getReference()?EntityManager.find() 和 EntityManger.getReference() 有什么区别? 【发布时间】:2011-07-25 19:48:47 【问题描述】:

有什么区别

<T> T EntityManager.find(Class<T> entityClass, Object primaryKey) and 
<T> T EntityManager.getReference(Class<T> entityClass, Object primaryKey) 

?

我认为 getReference 如果它是托管的,它会返回实体。 如果它是托管的,则 find 返回实体,否则在数据库上执行 SQL 以使其托管。

请确认。


上下文: 从 webapp 我得到要删除的对象的主键(长类型的 pk);到实体应该被管理删除。

EntityManager.remove(Object entity)

将托管实体传递给 entitymanager remove 方法'什么是更好和正确的选择?查找或获取引用?'

【问题讨论】:

这个类命名了一个不同的......其中一个与给牲畜喂食有关:-) When to use EntityManager.find() vs EntityManager.getReference()的可能重复 【参考方案1】:

如您所知,JPA 有 EntityManager 的概念。在实体管理器中工作期间,一些对象会从数据库中加载,可以修改并随后刷新到数据库中。

find() 必须返回对象的初始化实例。如果它尚未加载到 EntityManager 中,则从数据库中检索它。

getReference() 被允许返回一个代理而不是一个初始化的实例,如果该实体之前没有被加载到 EntityManager 中。在这个代理中,只有主键属性被初始化。 可以在不访问数据库的情况下创建代理,因为唯一的初始化属性已经提供给 getReference() 函数。

当您有一个实体 A 引用一个实体 B,并且您想将 A 的 b 属性设置为 B,而不必从数据库加载 B 时,后者很有用。

只有当你引用B的其他属性时,才会初始化代理。

【讨论】:

很好的解释。谢谢丹尼尔。如果实体已经加载,没有区别吗? 实际上 getReference 返回的对象不一定是任何“代理”,因为这是一个实现细节;有些将返回“代理”,而其他(支持字节码增强)将返回真实类型但延迟加载的对象。虽然您避免了调用此方法时的数据库命中,但稍后在加载字段时可能会获得多个数据库访问 一个需要提前注意的警告:将 getReference() 与继承结合可能会导致 'instanceof' 以预期的方式运行。有一些方法可以解决这个问题,主要是使用访问者模式,在这里讨论:***.com/questions/3540489/… @Daniel 如果 getReference 调用的实体中有版本注释会发生什么? 没什么特别的AFAIK,我们也使用@Version。并且版本不相关,因为它不是密钥的一部分。它仅在保存相关实体时使用。【参考方案2】:

Beginning Java EE 6 Platform with GlassFish 3这本书,在第135页提到不同之处:“Finding By ID”

find()如果找到实体,则返回;如果没有找到,则返回空值。

MyEntity obj = em.find(MyEntity.class, id);
if(obj != null)
   // Process the object 

getReference() 用于需要托管实体实例但没有数据(可能是实体的主键)被访问的情况。

try 
    MyEntity obj = em.getReference(MyEntity.class, id);
    // Process the object
 catch (EntityNotFoundException e) 
    // Entity Not Found

【讨论】:

【参考方案3】:

getReference() 不检索完整对象,而仅检索代理,因此如果您不访问对象的成员,效率会更高。

例如,在创建要插入数据库的新对象时,它可能必须引用已存储在数据库中的另一个对象。

为了让 JPA 正确存储新对象,只需要引用对象的主键。 通过使用getReference(),您可以获得一个包含主键的代理,并节省了加载完整对象的成本。

【讨论】:

以上是关于EntityManager.find() 和 EntityManger.getReference() 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

何时将 EntityManager.find() 与 EntityManager.getReference() 与 JPA 一起使用

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

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

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

Eclipselink 未检测到脏实体

如何查看 JPA 发出的 SQL 查询?