Session中save、update、saveOrUpdate、merge方法的区别是什么?

50

我是Hibernate的新手,上周学习了Hibernate教程。关于Session类中的save、update、saveOrUpdate和merge方法,我有几个疑问:

  • save方法:用于将新创建的对象插入数据存储区。(基本上这种情况下标识符的值为0)。例如,我创建了一个新的客户并调用了save操作,那么它会将其保存到数据存储区并生成标识符。

    这是正确的吗?如果我们对已经持久化的对象调用save方法,不确定会发生什么情况?

  • update方法:用于更新数据存储区中已经持久化的对象。(基本上这种情况下标识符的值为非零值)。例如,我加载了一个新的客户,并在更新某些字段值后调用update操作,它会在数据存储区中更新该值。

    据我所知,它应该因为API规定update是用于分离的对象而失败。这是正确的吗?如果是,如果对象不是分离的,我们应该调用什么来更新它呢?另一个要点是:如果我们在新创建的对象上调用update方法会发生什么情况?

  • saveOrUpdate方法:根据未保存值检查(它必须基于标识符的零值或非零值进行)调用上述任一方法,因此如果我们有持久化的客户对象并且更新了其姓氏并创建了一个新帐户,则saveOrUpdate将处理此操作。

    我理解得对吗?

  • merge方法:它的作用类似于update,但是如果具有相同标识符的持久化对象已经在会话内,则它会更新持久化对象中分离对象的值,并保存它。

    但是,如果当前与会话相关联的没有持久化实例,则会从数据存储区加载持久化对象,然后更新其中分离对象的值并将其保存。

    我的理解也正确吗?

7个回答

31

你绝大部分都是正确的,但是更新操作与你所描述的有些不同。如果一个对象在会话中(即持久化),那么更新就完全没有必要了。当会话被刷新时,Hibernate会处理对对象进行的任何更改并将其持久化。对它们调用更新将不起作用(并且可能会导致性能损失;我不确定)。

更新设计为在分离的对象上调用,即那些现在处于加载它们的会话之外的对象。@hvgotcodes的解释似乎是不正确的,因为只有当该对象不在会话中时,才应该调用更新。如果对象的实例已经在会话中,则更新可能失败。此时应使用合并操作(Merge)。如果它存在于会话中,则将分离对象的更改与会话中的对象合并。如果会话中没有对象,则会创建一个新对象。

因此,通常可以避免完全调用更新/合并,但如果您最终必须调用其中之一,则合并操作处理范围更广。我的理解是,使用更新操作的唯一原因是为了更好的性能,假设您知道它不会出错。

这个线程对一些其他Hibernate方法也有很好的总结。

编辑:我认为我应该说合并和更新之间还有更多的差异。更新操作修改给定实体以使其持久化,而合并操作返回一个新的持久化实体。使用合并操作时,应丢弃旧对象。另一个区别是,在决定是否写入数据之前,合并操作通过从数据库中选择来进行脏检查,而更新操作始终将其数据持久化到数据库中,无论它是否脏。

可能还有其他小差异。根据我的经验,通过记录生成的SQL来测试Hibernate行为总是很有帮助,因为行为不总是与文档匹配。


“而更新操作并不总是将其数据持久化到数据库中,无论它是否已更改” - 这并不是真的。如果您实现了CustomEntityDirtinessStrategy,您可以通过session.update获得两个世界的最佳体验:不触发额外的select,也不会在未更改的实体上触发更新。 - Aleksandr

11

你在所有评估方面都完全正确,你懂了。

对于你的第一个问题,如果我记得正确的话,save 特别执行插入操作。因此,再次调用 save 将在数据库中产生另一行。

对于你的第二个问题,update 更新会话中的对象。因此,如果对象在会话中,则会更新该对象。如果对象不在会话中,应该调用合并。我认为,针对分离的实例调用 update 会导致异常。


谢谢hvgotcodes。如果你能给出关于灰色/引用部分的简短答案,那将非常有帮助。 - M Sach
嗨hvgotcodes,我认为第一和第二个解释都不正确。因为保存检查必须检查零的标识符值,但对于已持久化的对象,它不是零。因此,它不应该插入新行。关于第二个答案,根据API文档,更新也适用于分离的对象。不确定如果我们更新已经持久化的对象,应该调用哪种方法? - M Sach
@mohit,你真的尝试过这些吗?写一个单元测试来试试。 - hvgotcodes
你的第一个答案是错误的。通过多次调用save(object),Hibernate不会向数据库触发多个查询,因为Hibernate将使用其一级缓存(Session),而Hibernate在代理上工作。 - subhashis
实际上,第一个和第二个答案都是错误的。在会话中两次调用保存只会导致一次插入,对持久化对象调用更新是不必要的但不会引发异常,当然你也可以对分离对象调用更新。 - pzeszko

8

@Naliba给出了关于Update()方法的优秀答案。

下面的图片展示了Hibernate生命周期,有助于理解这些方法。

enter image description here

例子:让我们看看merge()方法的情况。

SessionFactory factory = cfg.buildSessionFactory();
Session session1 = factory.openSession();

Student student1 = null;
Object object1 = session1.get(Student.class, new Integer(101));
student1 = (Student)object1;
session1.close();

student1.setMarks(97);// -->object will be in some RAM location, not in the session1 cache

Session session2 = factory.openSession();
Student student2 = null;
Object object2 = session2.get(Student.class, new Integer(101));
student2 = (Student)object2;
Transaction tx=session2.beginTransaction();

session2.merge(student1);

上述的student1处于分离状态,修改了分离对象student1,现在如果我们调用update()方法,那么Hibernate将会抛出一个错误。

在这个session2中,我们调用了session2.merge(s1);,现在student1对象的更改将会被合并并保存到数据库中的student2对象中。


4

update() 方法用于处理游离状态的对象和瞬时状态的对象。如果在持久化状态的对象上调用此方法,会抛出一个 NonUniqueObjectException 异常,可以通过使用 merge() 方法来解决。


我认为_update_不适用于_transient_对象。如果您在_transient_对象上调用update,将会抛出StaleStateException异常。 - Ken Block

4

session.update() - 该方法用于以下场景:您已从Hibernate会话中加载了一个名为Person1的对象。现在该对象正在应用程序中使用,可能还在客户端上使用,并且已经被更新。我们想要再次保存它。我们知道数据中的Person对象没有进行任何更改。因此,我们可以简单地使用update方法。

session.merge() - 在上述情况下,如果在保存更改后对person数据进行了更改,则应使用merge。它将合并更改。

session.save() - 它可用于保存新对象。它返回可序列化的标识。

session.persist() - 它与save()相同,但是它是void方法,不返回任何内容。

session.saveOrUpdate() - 该方法适用于新旧对象。如果对象是新的,则它将像简单的保存一样工作;如果对象已经存在,则它将像更新一样工作。

session.lock() - 它仅用于锁定对象或者说检查对象的版本。它不是用于更新对象的。只有在您确定对象状态尚未在数据库中更改时,才应该使用它来重新附加对象。否则,它可能会覆盖更改。<欢迎在此基础上添加更多观点。


3

合并操作的执行步骤如下:

合并操作是智能的。在实际进行合并之前,它会进行很多预检查(如果需要)。

  1. 如果对象是瞬态的,则它会简单地触发INSERT查询,使对象持久化(附加到会话中)。
  2. 如果对象是游离的,则会触发SELECT查询以检查数据是否已修改 如果已修改,则触发UPDATE查询,否则只忽略合并任务。

而对于session.update操作:

  1. 如果对象是瞬态的,则会抛出异常。
  2. 如果对象是游离的,则无论数据变化与否,它都会简单地触发UPDATE查询。

可以看出,session.merge操作比update操作更耗时。


0
你应该在你的代码中应用save()和saveOrUpdate()方法之间的区别以获得最佳性能:
save()方法返回数据库生成的标识符。另一方面,saveOrUpdate()方法可以根据对象是否存在于数据库中执行INSERT或UPDATE操作。而saveOrUpdate()先进行选择以确定是否需要进行插入或更新。所以在更新查询的情况下应该使用saveOrUpdate()。
save()和saveOrUpdate()方法之间的另一个关键区别是,save()方法用于将瞬态对象转换为持久状态,但saveOurUpdate()可以将瞬态(新对象)和分离的(现有对象)对象都转换为持久状态。因此,saveOrUpdate()通常用于将分离的对象重新附加到Session中。
来自Hibernate中save和saveOrUpdate之间的区别的文章

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接