Hibernate的save、persist、update、merge和saveOrUpdate
原文地址:http://www.baeldung.com/hibernate-save-persist-update-merge-saveorupdate。
1.介绍
在这篇文章中,我们将讨论session接口的几种方法之间的差异:save、persist、update、merge和saveOrUpdate。
这不是Hibernate的介绍,你应该已经知道配置,对象关系映射和使用实体实例的基础知识。对于一个介绍性的文章冬眠,请访问我们的教程Hibernate 4 with Spring。
2.会话作为持久化上下文实现
该会话接口有多种方法,最终导致数据保存到数据库中:persist,save,update,merge,saveOrUpdate。要了解这些方法之间的区别,我们首先要讨论的目的,会议作为一个持久化上下文,并相对于实体实例的状态之间的差异会话。
我们还应该了解Hibernate开发的历史,导致一些部分重复的API方法。
2.1。管理实体实例
除了对象关系映射本身,Hibernate打算解决的问题之一是在运行时期间管理实体的问题。“持久化上下文”的概念是Hibernate的这个问题的解决方案。对于在会话期间加载或保存到数据库的所有对象,持久性上下文可以被视为容器或第一级缓存。
会话是一个逻辑事务,其边界由应用程序的业务逻辑定义。当您通过持久化上下文处理数据库,并且所有实体实例都附加到此上下文时,对于您在会话期间已交互的每个数据库记录,您应始终拥有一个实体实例。
在Hibernate中,持久化上下文所代表的org.hibernate.Session实例。对于JPA,它是javax.persistence.EntityManager。当我们使用Hibernate作为JPA提供,并通过运营EntityManager的接口,这个接口的实现基本上包装了基本会话对象。然而,Hibernate的会话提供了更多的可能性更丰富的接口,所以有时它一起工作有用的直接会话。
2.2。实体实例状态
在应用程序中的任何实体实例出现在有关的三个主要状态之一 会话持久化上下文:
- 瞬态 -这种情况下现在没有、过去也没有连接到一个会话上 ; 此实例在数据库中没有相应的行; 它通常只是一个你创建的新对象保存到数据库;
- 持久态 -此实例具有独特的关联会话对象; 在flush会话到数据库中,这个实体是保证在数据库中有一个相应的记录一致;
- 脱离态 -这种情况下一度被连接到一个会话(在一个持续状态),但现在它不是; 如果从上下文中驱逐它,清除或关闭会话,或者通过序列化/反序列化过程放置实例,实例将进入此状态。
下面是对注释的简化的状态图会话方法,使转换发生的状态。
![2016-07-11_13-38-11](http://www.baeldung.com/wp-content/uploads/2016/07/2016-07-11_13-38-11-1024x551.png)
当实体实例处于持续状态,您对这个实例的映射字段的所有更改将在flush被应用到相应的数据库记录和字段会议。在持续的实例可以被认为是“在线”,而分离的情况下已经“下线”,而不是监测变化。
这意味着,当你改变一个领域持续对象,你不必调用保存,更新或任何这些方法让这些对数据库的更改:你需要的是提交事务,或刷新或关闭会话,当你完成它。
2.3。符合JPA规范
Hibernate是最成功的Java ORM实现。难怪Java持久性API(JPA)的规范受到Hibernate API的严重影响。不幸的是,也有许多区别:一些主要,一些更微妙。
为了充当JPA标准的实现,必须修改Hibernate API。会话接口添加了几个方法来匹配EntityManager接口。这些方法具有与“原始”方法相同的目的,但符合规范,因此具有一些差异。
3.操作之间的差异
从一开始,所有的方法(要了解是很重要的persist,save,update,merge,saveOrUpdate)不会立即产生相应的SQL UPDATE或INSERT语句。数据到数据库的实际节电发生在提交事务或冲洗的会话。
所提到的方法基本上通过在实体实例的生命周期中在不同状态之间转换来管理实体实例的状态。
作为一个例子实体,我们将使用一个简单的注解映射实体的人:
@Entity
publicclassPerson {
@Id
@GeneratedValue
privateLong id;
privateString name;
// ... getters and setters
}
3.1。persist
该persist的方法是用于添加一个新的实体实例的持久化上下文,即从短暂的过渡一个实例持续状态。
我们通常在我们要向数据库添加记录时调用它(持久化实体实例):
Person person = newPerson();
person.setName("John");
session.persist(person);
之后会发生什么坚持方法被调用?该人对象已经转变暂态到持续状态。该对象现在在持久性上下文中,但尚未保存到数据库。INSERT语句的产生,仅在commiting事务,flush或关闭会话会发生。
请注意,persist方法有void的返回类型。它对所传递的对象“就地”操作,改变其状态。该person变量引用实际的持久对象。
此方法是稍后添加到Session接口。这种方法的主要区别特征是它符合JSR-220规范(EJB持久性)。该方法的语义在规范中严格定义,其基本上规定:
- 一个瞬态的实例变为持续态(和操作级联到其所有的关系与级联= PERSIST或级联= ALL),
- 如果一个实例已经处于持久态,那么此调用此特定实例没有影响(但它仍然级联到它的关系,与级联= PERSIST或级联= ALL),
- 如果一个实例是分离态,你应该期待一个异常,无论是在调用此方法,或者在提交或flush会话。
请注意,这里没有涉及实例的标识符。规范没有声明id将立即生成,而不考虑id生成策略。persist方法的规范允许在提交或刷新时执行发出一条生成的ID语句,而调用此方法后生成的这个ID保证非空,所以你不应该依赖于它。
你可以在一个持久化实例上调用这个方法,并没有任何反应。但是,如果你尝试persist一个脱离态的实例,这个执行势必会抛出异常。在下面的例子中,我们持久化一个实体,然后从上下文中evict它,所以它变成脱离态,然后尝试persist一次。第二次调用 session.persist()导致异常,所以下面的代码将无法正常工作:
person.setName("John");
session.persist(person);
session.evict(person);
session.persist(person);// PersistenceException!
3.2。 save
save方法是“原始”的Hibernate方法,但是不符合JPA规范的。
它的目的是基本相同持续,但它具有不同的实施细节。此方法的文档严格说明它保留实例“首次分配生成的标识符”。该方法是保证返回 序列化这个标识符的值。
person.setName("John");
Long id = (Long) session.save(person);
save一个已经持续实例的效果是一样的,与persist。区别在于,当您尝试保存一个独立的实例:
person.setName("John");
Long id1 = (Long) session.save(person);
session.evict(person);
Long id2 = (Long) session.save(person);
|
该ID2变量也会有所不同ID1。上面save的调用会给脱离态的实例创建一个新的持久化实例,并赋予它一个新的标识符,在提交或flush时会导致重复的记录在数据库中。
3.3。 merge
merge方法的主要意图是使用一个脱离态实体实例的字段值,去更新一个持久态的实体实例。
例如,假设您有一个RESTful接口,其中包含一个通过其id向检索者检索JSON序列化对象的方法,以及从调用者接收此对象的更新版本的方法。一个实例通过这种序列化传递的实体/反序列化会处在一个脱离的状态。
在反序列化这个实体实例之后,你需要从一个持久化上下文中获得一个持续态的实体实例,并且从脱离态的实体实例中获得值去更新它的字段。因此,merge的方法正是这么做的:
- 通过id从所传递的对象中获取实体实例(从持久上下文中检索现有的实体实例,或者从数据库中加载新实例);
- 将字段从传递的对象复制到此实例;
- 返回新更新的实例。
在下面的例子中,我们驱逐 (分离)从上下文中保存的实体,更改名称字段,然后合并的独立实体。
person.setName("John");
session.save(person);
session.evict(person);
person.setName("Mary");
Person mergedPerson = (Person) session.merge(person);
请注意,合并方法返回一个对象-它是mergedPerson被加载到持久化上下文和update,而并不是你传递过去的person对象。这是两个不同的对象,而person的目标通常需要被丢弃(不要指望它重新被连接到持久化上下文)。
与persist方法一起,merge是由JSR-220规定的,有一定的语义:
- 如果实体是脱离态,它会被拷贝到一个已经存在的持久态的实体上;
- 如果实体是瞬态的,它是被拷贝到一个新创建的持久实体态实体上;
- 此操作级联所有cascade= MERGE或cascade= ALL映射关系;
- 如果实体是持久态的,则此方法调用没有任何影响(但仍级联发生)。
3.4。 update
就像persist和save一样,update方法是一种“原始的”hibernate方法,是merge加入之前使用很久的方法。它的语义在几个关键点有所不同:
- 它作用于传递的对象(它的返回类型是void); 它把传递的对象从脱离态转换成持久态;
- 如果你传递一个此方法一个瞬态实体,会抛出一个异常。
在下面的例子中,我们保存对象,然后驱逐从上下文(脱离),然后更改其名称和调用update。请注意,我们不要把结果的更新操作在一个单独的变量,因为update发生在person的对象本身。基本上,我们将现有的实体实例重新挂接到持久化上下文 - JPA规范不允许我们这样做。
person.setName("John");
session.save(person);
session.evict(person);
person.setName("Mary");
session.update(person);
|
试图调用更新一个短暂的实例将导致异常。以下不工作:
person.setName("John");
session.update(person);// PersistenceException!
|
3.5。SaveOrUpdate
此方法仅出现在Hibernate API中,没有标准化的对应方法。类似于update,它也可以用于重新连接实例。
其实,内部 DefaultUpdateEventListener 处理该级更新方法的一个子类DefaultSaveOrUpdateListener,只是覆盖一些功能。saveOrUpdate方法的主要区别是,当它应用到一个瞬态实例时,不抛出异常; 相反,它使这个瞬态持续化。下面的代码将持续的新创建的实例人:
person.setName("John");
session.saveOrUpdate(person);
|
你可能会认为这种方法是可以使对象持久化的通用工具,无论其状态是瞬态的或脱落态。
4.使用什么?
如果你没有任何特别的要求,作为一个经验法则,你应该坚持使用persist和merge方法,因为它们是标准化,保证符合JPA规范。
对于你决定要切换到另一种持久性方案提供商的情况,他们也是很方便的。但他们可能有时没有“原始”Hibernate方法看上去这么有用:save,update和saveOrUpdate。
5.结论
我们讨论了与在运行时管理持久化实体相关的不同Hibernate Session方法的目的。我们已经了解了这些方法如何通过它们的生命周期转换实体实例,以及为什么这些方法具有重复的功能。
posted on 2017-01-05 12:42 chaiyu2002 阅读(234) 评论(0) 编辑 收藏 举报