JPA 和 Hibernate 中的 persist() 和 merge() 有啥区别?

Posted

技术标签:

【中文标题】JPA 和 Hibernate 中的 persist() 和 merge() 有啥区别?【英文标题】:What is the difference between persist() and merge() in JPA and Hibernate?JPA 和 Hibernate 中的 persist() 和 merge() 有什么区别? 【发布时间】:2011-05-29 09:19:06 【问题描述】:

Hibernate中persist()和merge()有什么区别?

persist() 可以创建一个 UPDATE & INSERT 查询,例如:

SessionFactory sef = cfg.buildSessionFactory();
Session session = sef.openSession();
A a=new A();
session.persist(a);
a.setName("Mario");
session.flush();

在这种情况下,查询将像这样生成:

Hibernate: insert into A (NAME, ID) values (?, ?)
Hibernate: update A set NAME=? where ID=?

所以persist() 方法可以生成插入和更新。

现在merge()

SessionFactory sef = cfg.buildSessionFactory();
Session session = sef.openSession();
Singer singer = new Singer();
singer.setName("Luciano Pavarotti");
session.merge(singer);
session.flush();

这是我在数据库中看到的:

SINGER_ID   SINGER_NAME
1           Ricky Martin
2           Madonna
3           Elvis Presley
4           Luciano Pavarotti

现在使用merge()更新记录

SessionFactory sef = cfg.buildSessionFactory();
Session session = sef.openSession();
Singer singer = new Singer();
singer.setId(2);
singer.setName("Luciano Pavarotti");
session.merge(singer);
session.flush();

这是我在数据库中看到的:

SINGER_ID   SINGER_NAME
1           Ricky Martin
2           Luciano Pavarotti
3           Elvis Presley

【问题讨论】:

javadoc 非常明确地说明了它们的作用以及它们之间的区别。你读过并理解了吗? 检查***.com/questions/161224/… ***.com/questions/1069992/… 【参考方案1】:

JPA specification 包含对这些操作语义的非常精确的描述,比在 javadoc 中更好:

persist 的语义 操作,应用于实体 X 是 如下:

如果 X 是一个新实体,它 成为管理的。实体 X 将是 在或之前输入数据库 事务提交或由于 刷新操作。

如果 X 是 预先存在的管理实体,它是 被持久操作忽略。 但是,持久化操作是 级联到 X 引用的实体, 如果从 X 到这些的关系 其他实体用 cascade=PERSISTcascade=ALL 注释元素值或指定 使用等效的 XML 描述符 元素。

如果 X 是已移除的实体, 它变得受管理。

如果 X 是 分离物体 EntityExistsException 可能会被抛出 当调用持久化操作时, 或EntityExistsException 或 另一个PersistenceException 可能是 在刷新或提交时抛出。

对于 a 引用的所有实体 Y 来自 X 的关系,如果 与 Y 的关系已被注释 具有级联元素值 cascade=PERSISTcascade=ALL, 对 Y 应用持久化操作。


合并操作的语义 应用于实体 X 如下:

如果 X 是分离的实体,则状态 的 X 被复制到预先存在的 相同的托管实体实例 X' 身份或 X 的新托管副本 X' 已创建。

如果 X 是一个新实体 实例,一个新的托管实体 实例 X' 被创建并且状态 的 X 被复制到新的托管 实体实例 X'。

如果 X 是 移除的实体实例,一个 IllegalArgumentException 将是 由合并操作(或 事务提交将失败)。

如果 X 是一个托管实体,它被忽略 合并操作,然而, 合并操作被级联到 关系引用的实体 从 X 如果这些关系有 被级联注释 元素值cascade=MERGEcascade=ALL注解。

为所有人 关系引用的实体 Y 从具有级联元素的 X 值cascade=MERGEcascade=ALL, Y 递归地合并为 Y'。对所有人 X 引用的这样的 Y,X' 设置为 参考 Y'。 (注意,如果 X 是 托管然后 X 是相同的对象 X'。)

如果 X 是合并到 X' 的实体, 引用另一个实体 Y, 其中cascade=MERGEcascade=ALL 是 未指定,则导航 来自 X' 的相同关联产生 对托管对象 Y' 的引用 与 Y 相同的持久身份。

【讨论】:

感谢您的信息。我看到了这两个定义的语义。但问题是关于它们之间的差异。也许为persistmerge 的每个不同行为提供状态列表和2 个子部分?【参考方案2】:

来自JPA。用一种非常简单的方式:

persist(entity) 应该与全新的实体一起使用,以将它们添加到数据库中(如果实体已经存在于数据库中,则会抛出 EntityExistsException)。

应该使用merge(entity),如果实体被分离并被更改,则将实体放回持久性上下文。

【讨论】:

您能否在解释中添加来源?谢谢。 @AlikElzin-kilaka 这样的解释,我记得,我在“Beginning Java EE 7”一书中找到了。【参考方案3】:

仅应在新实体上调用 Persist,而 merge 旨在重新附加分离的实体。

如果您使用分配的生成器,使用 merge 而不是 persist 可能会导致冗余 SQL 语句。

此外,对托管实体调用合并也是一个错误,因为托管实体由 Hibernate 自动管理,并且它们的状态在刷新持久性上下文时通过脏检查机制与数据库记录同步。

【讨论】:

【参考方案4】:

最重要的区别是:

persist 方法的情况下,如果要在持久化上下文中管理的实体已经存在于持久化上下文中,则忽略新的实体。 (什么都没发生)

但在merge 方法的情况下,已在持久性上下文中管理的实体将被新实体(更新)替换,并且此更新实体的副本将返回。 (从现在开始,如果您想在持久性上下文中反映您的更改,则应在此返回的实体上进行任何更改)

【讨论】:

以上是关于JPA 和 Hibernate 中的 persist() 和 merge() 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

来自 JpaRepository 接口的查询

hibernate/JPA 中的 @PreUpdate 和 @Prepersist(使用会话)

JPA 和 Hibernate 中的 N+1 问题的解决方案是啥?

有人可以解释 JPA 和 Hibernate 中的 mappedBy 吗?

from 子句中的 JPA/hibernate 子查询

从 JPA/Hibernate 中的视图加载实体