ChatGPT解决这个技术问题 Extra ChatGPT

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

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 非常明确地说明了它们的作用以及它们之间的区别。你读过并理解了吗?

a
axtavt

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

应用于实体 X 的持久操作的语义如下: 如果 X 是新实体,则它成为托管实体。实体 X 将在事务提交时或之前或作为刷新操作的结果输入到数据库中。如果 X 是预先存在的托管实体,则持久操作将忽略它。但是,如果使用 cascade=PERSIST 或 cascade=ALL 注释元素值注释从 X 到这些其他实体的关系或使用等效的 XML 描述符元素指定,则持久操作将级联到 X 引用的实体。如果 X 是一个被移除的实体,它就会变成托管的。如果 X 是一个分离的对象,则在调用持久操作时可能会抛出 EntityExistsException,或者在刷新或提交时可能会抛出 EntityExistsException 或另一个 PersistenceException。对于由来自 X 的关系引用的所有实体 Y,如果与 Y 的关系已使用级联元素值 cascade=PERSIST 或 cascade=ALL 进行注释,则将持久操作应用于 Y。

应用于实体 X 的合并操作的语义如下: 如果 X 是分离实体,则将 X 的状态复制到具有相同身份的预先存在的托管实体实例 X' 或新的托管副本 X' 上X 已创建。如果 X 是一个新的实体实例,则创建一个新的托管实体实例 X',并将 X 的状态复制到新的托管实体实例 X'。如果 X 是已移除的实体实例,则合并操作将抛出 IllegalArgumentException(或事务提交将失败)。如果 X 是一个托管实体,则合并操作将忽略它,但是,如果这些关系已使用级联元素值 cascade=MERGE 或 cascade=ALL 注释进行注释,则合并操作将级联到由来自 X 的关系引用的实体。对于由来自 X 的具有级联元素值 cascade=MERGE 或 cascade=ALL 的关系引用的所有实体 Y,Y 被递归地合并为 Y'。对于由 X 引用的所有此类 Y,X' 设置为引用 Y'。 (请注意,如果 X 是托管的,则 X 是与 X' 相同的对象。)如果 X 是合并到 X' 的实体,并引用另一个实体 Y,其中未指定 cascade=MERGE 或 cascade=ALL,则导航来自 X' 的相同关联产生对具有与 Y 相同的持久标识的托管对象 Y' 的引用。


谢谢(你的)信息。我看到了这两个定义的语义。但问题是关于它们之间的差异。也许为 persistmerge 的每种不同行为提供状态列表和 2 个子部分?
C
CodeSlave

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

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

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


你能在你的解释中添加一个来源吗?谢谢。
@AlikElzin-kilaka 这样的解释,我记得,我在“Beginning Java EE 7”一书中找到。
V
Vlad Mihalcea

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

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

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


U
Uwe Allner

最重要的区别在于:

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

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