Hibernate: save, persist, update, merge, saveOrUpdate – Hibernate:保存、坚持、更新、合并、saveOrUpdate

最后修改: 2016年 7月 12日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial, we’ll discuss the differences between several methods of the Session interface: save, persist, update, merge, and saveOrUpdate.

在本教程中,我们将讨论Session接口的几个方法之间的区别。savepersistupdatemergesaveOrUpdate

This isn’t an introduction to Hibernate, and we should already know the basics of configuration, object-relational mapping, and working with entity instances. For an introductory article to Hibernate, visit our tutorial on Hibernate 4 with Spring.

这不是对Hibernate的介绍,我们应该已经知道了配置、对象关系映射和处理实体实例的基本知识。有关Hibernate的介绍性文章,请访问我们的Hibernate 4 with Spring教程。

2. Session as a Persistence Context Implementation

2.会话作为持久性上下文的实现

The Session interface has several methods that eventually result in saving data to the database: persist, save, update, merge, and saveOrUpdate. To understand the difference between these methods, we must first discuss the purpose of the Session as a persistence context, and the difference between the states of entity instances in relation to the Session.

Session接口有几个方法,最终会导致将数据保存到数据库。persist, save, update, merge, 和 saveOrUpdate.为了理解这些方法之间的区别,我们必须首先讨论Session作为持久化上下文的目的,以及实体实例的状态与Session的区别。

We should also understand the development history of Hibernate that led to some partly duplicated API methods.

我们还应该了解Hibernate的发展历史,它导致了一些部分重复的API方法。

2.1. Managing Entity Instances

2.1.管理实体实例

Apart from object-relational mapping itself, one of the problems that Hibernate solves is the problem of managing entities during runtime. The notion of “persistence context” is Hibernate’s solution to this problem. We can think of persistence context as a container or first-level cache for all the objects that we loaded or saved to a database during a session.

除了对象关系映射本身,Hibernate解决的问题之一是在运行时管理实体的问题。持久性上下文 “的概念是Hibernate对这个问题的解决方案。我们可以把持久化上下文看作是我们在会话中加载或保存到数据库中的所有对象的容器或一级缓存。

The session is a logical transaction, with boundaries defined by the application’s business logic. When we work with the database through a persistence context, and all of our entity instances are attached to this context, we should always have a single instance of entity for every database record that we interact with during the session.

会话是一个逻辑事务,其边界由应用程序的业务逻辑定义。当我们通过持久化上下文与数据库一起工作,并且我们所有的实体实例都附着在这个上下文上时,我们在会话中与之交互的每一条数据库记录都应该有一个实体实例。

In Hibernate, the persistence context is represented by the org.hibernate.Session instance. For JPA, it’s the javax.persistence.EntityManager. When we use Hibernate as a JPA provider, and operate via the EntityManager interface, the implementation of this interface basically wraps the underlying Session object. However, Hibernate Session provides a richer interface with more possibilities, so sometimes it’s useful to work with Session directly.

在Hibernate中,持久化上下文由org.hibernate.Session实例表示。对于JPA,它是javax.persistence.EntityManager。当我们使用Hibernate作为JPA提供者,并通过EntityManager接口进行操作时,该接口的实现基本上包裹了基础Session对象。然而,Hibernate的Session提供了一个更丰富的接口,有更多的可能性,所以有时直接与Session一起工作是很有用的。

2.2. States of Entity Instances

2.2.实体实例的状态

Any entity instance in our application appears in one of the three main states in relation to the Session persistence context:

在我们的应用程序中,任何实体实例都出现在与Session持久化上下文有关的三个主要状态之一。

  • transient — This instance isn’t, and never was, attached to a Session. This instance has no corresponding rows in the database; it’s usually just a new object that we created to save to the database.
  • persistent — This instance is associated with a unique Session object. Upon flushing the Session to the database, this entity is guaranteed to have a corresponding consistent record in the database.
  • detached — This instance was once attached to a Session (in a persistent state), but now it’s not. An instance enters this state if we evict it from the context, clear or close the Session, or put the instance through serialization/deserialization process.

Here’s a simplified state diagram with comments on Session methods that make the state transitions happen:

下面是一个简化的状态图,其中有关于使状态转换发生的Session方法的评论。

2016-07-11_13-38-11

When the entity instance is in the persistent state, all the changes that we make to the mapped fields of this instance will be applied to the corresponding database records and fields upon flushing the Session. The persistent instance is “online,” whereas the detached instance is “offline” and not monitored for changes.

当实体实例处于persistent状态时,我们对该实例的映射字段所做的所有更改将在刷新Session时应用于相应的数据库记录和字段。persistent实例是 “在线 “的,而detached实例是 “离线 “的,不被监控变化。

This means that when we change the fields of a persistent object, we don’t have to call save, update, or any of those methods to get these changes to the database. All we need to do is commit the transaction, flush the session, or close the session.

这意味着当我们改变persistent对象的字段时,我们不需要调用saveupdate或任何这些方法来将这些改变带到数据库中。我们所要做的就是提交事务,刷新会话,或者关闭会话。

2.3. Conformity to JPA Specification

2.3.与JPA规范的一致性

Hibernate was the most successful Java ORM implementation. As such, the Hibernate API heavily influenced the specifications for the Java persistence API (JPA). Unfortunately, there were also many differences, some major and some more subtle.

Hibernate是最成功的Java ORM实现。因此,Hibernate API在很大程度上影响了Java持久化API(JPA)的规范。不幸的是,也有许多不同之处,有些是主要的,有些是比较微妙的。

To act as an implementation of the JPA standard, Hibernate APIs had to be revised. To match the EntityManager interface, several methods were added to the Session interface. These methods serve the same purpose as the original methods, but conform to the specification, and thus have some differences.

为了充当JPA标准的执行者,Hibernate的API必须被修改。为了配合EntityManager接口,在Session接口中添加了几个方法。这些方法的目的与原来的方法相同,但符合规范,因此有一些区别。

3. Differences Between the Operations

3.业务之间的差异

It’s important to understand from the beginning that all of the methods (persist, save, update, merge, saveOrUpdate) don’t immediately result in the corresponding SQL UPDATE or INSERT statements. The actual saving of data to the database occurs upon committing the transaction or flushing the Session.

从一开始就要明白,所有的方法(persist, save, update, merge, saveOrUpdate)都不会立即产生相应的SQLUPDATEINSERT语句。数据的实际保存是在提交事务或刷新Session时进行的。

The mentioned methods basically manage the state of entity instances by transitioning them between different states along the lifecycle.

所提到的方法基本上是通过在生命周期的不同状态之间过渡来管理实体实例的状态。

As an example, we’ll use a simple annotation-mapped entity, Person:

作为一个例子,我们将使用一个简单的注释映射的实体,Person

@Entity
public class Person {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    // ... getters and setters

}

3.1. Persist

3.1.坚持

The persist method is intended to add a new entity instance to the persistence context, i.e. transitioning an instance from a transient to persistent state.

persist方法旨在将一个新的实体实例添加到持久化上下文中,即把一个实例从transient过渡到persistent状态。

We usually call it when we want to add a record to the database (persist an entity instance):

我们通常在想向数据库添加一条记录时调用它(坚持一个实体实例)。

Person person = new Person();
person.setName("John");
session.persist(person);

What happens after we call the persist method? The person object has transitioned from a transient to persistent state. The object is in the persistence context now, but not yet saved to the database. The generation of INSERT statements will occur only upon committing the transaction, or flushing or closing the session.

在我们调用persist方法后会发生什么?person对象已经从transient过渡到persistent状态。该对象现在在持久化上下文中,但尚未保存到数据库中。INSERT语句的生成将只在提交事务、冲刷或关闭会话时发生。

Notice that the persist method has a void return type. It operates on the passed object “in place,” changing its state. The person variable references the actual persisted object.

注意,persist方法有一个void返回类型。它在传递的对象上 “就地 “操作,改变其状态。person变量引用了实际被持久化的对象。

This method is a later addition to the Session interface. The main differentiating feature of this method is that it conforms to the JSR-220 specification (EJB persistence). We strictly define the semantics of this method in the specification, which basically states that a transient instance becomes persistent (and the operation cascades to all of its relations with cascade=PERSIST or cascade=ALL):

这个方法是后来添加到Session接口的。这个方法的主要区别在于,它符合JSR-220规范(EJB持久化)。我们在规范中严格定义了这个方法的语义,基本上是说一个transient实例变成persistent(并且这个操作会级联到它的所有关系中,并带有cascade=PERSISTcascade=ALL)。

  • if an instance is already persistent, then this call has no effect for this particular instance (but it still cascades to its relations with cascade=PERSIST or cascade=ALL).
  • if an instance is detached, we’ll get an exception, either upon calling this method, or upon committing or flushing the session.

Notice that there’s nothing here that concerns the identifier of an instance. The spec doesn’t state that the id will generate right away, regardless of the id generation strategy. The specification for the persist method allows the implementation to issue statements to generate the id on commit or flush. The id won’t necessarily be non-null after we call this method, so we shouldn’t rely upon it.

请注意,这里没有任何关于实例标识符的内容。规范中并没有说明无论采用何种id生成策略,id都会立即生成。persist方法的规范允许实现在提交或刷新时发布语句来生成id。在我们调用这个方法后,id不一定是非空的,所以我们不应该依赖它。

We may call this method on an already persistent instance, and nothing happens. But if we try to persist a detached instance, the implementation will throw an exception. In the following example, we’ll persist the entity, evict it from the context so it becomes detached, and then try to persist again. The second call to session.persist() causes an exception, so the following code won’t work:

我们可以在一个已经持久化的实例上调用这个方法,而什么也不会发生。但是如果我们试图持久化一个detached实例,该实现将抛出一个异常。在下面的例子中,我们将持久化实体,从上下文中驱逐它,使其成为detached,然后再次尝试持久化。第二次调用session.persist()会导致一个异常,所以下面的代码将无法工作。

Person person = new Person();
person.setName("John");
session.persist(person);

session.evict(person);

session.persist(person); // PersistenceException!

3.2. Save

3.2. 保存

The save method is an “original” Hibernate method that doesn’t conform to the JPA specification.

save方法是Hibernate的一个 “原始 “方法,不符合JPA规范。

Its purpose is basically the same as persist, but it has different implementation details. The documentation for this method strictly states that it persists the instance, “first assigning a generated identifier.” The method will return the Serializable value of this identifier:

它的目的与persist基本相同,但它有不同的实现细节。这个方法的文档严格指出,它持久化实例,”首先分配一个生成的标识符”。该方法将返回该标识符的Serializable值。

Person person = new Person();
person.setName("John");
Long id = (Long) session.save(person);

The effect of saving an already persisted instance is the same as with persist. The difference comes when we try to save a detached instance:

保存一个已经持久化的实例的效果与persist相同。当我们试图保存一个detached实例时,差别就来了。

Person person = new Person();
person.setName("John");
Long id1 = (Long) session.save(person);

session.evict(person);
Long id2 = (Long) session.save(person);

The id2 variable will differ from id1. The save call on a detached instance creates a new persistent instance and assigns it a new identifier, which results in a duplicate record in the database upon committing or flushing.

id2变量将与id1不同。对detached实例的保存调用会创建一个新的persistent实例,并给它分配一个新的标识符,这将导致在提交或刷新时在数据库中出现重复记录。

3.3. Merge

3.3.合并

The main intention of the merge method is to update a persistent entity instance with new field values from a detached entity instance.

merge方法的主要目的是用来自detached实体实例的新字段值来更新persistent实体实例。

For instance, suppose we have a RESTful interface with a method for retrieving a JSON-serialized object by its id to the caller, and a method that receives an updated version of this object from the caller. An entity that passed through such serialization/deserialization will appear in a detached state.

例如,假设我们有一个RESTful接口,其中有一个方法用于通过其id向调用者检索一个JSON序列化的对象,还有一个方法用于从调用者那里接收这个对象的更新版本。通过这种序列化/反序列化的实体将以detached状态出现。

After deserializing this entity instance, we need to get a persistent entity instance from a persistence context and update its fields with new values from this detached instance. So the merge method does exactly that:

在反序列化这个实体实例后,我们需要从持久化上下文中获得一个持久化实体实例,并用这个分离实例的新值更新它的字段。因此,merge方法正是这样做的。

  • finds an entity instance by id taken from the passed object (either an existing entity instance from the persistence context is retrieved, or a new instance loaded from the database)
  • copies fields from the passed object to this instance
  • returns a newly updated instance

In the following example, we evict (detach) the saved entity from the context, change the name field, and then merge the detached entity:

在下面的例子中,我们从上下文中evict(分离)保存的实体,改变name字段,然后merge detached实体。

Person person = new Person(); 
person.setName("John"); 
session.save(person);

session.evict(person);
person.setName("Mary");

Person mergedPerson = (Person) session.merge(person);

Note that the merge method returns an object. It’s the mergedPerson object we loaded into the persistence context and updated, not the person object that we passed as an argument. They’re two different objects, and we usually need to discard the person object.

请注意,merge方法返回一个对象。它是我们加载到持久化上下文并更新的mergedPerson对象,而不是我们作为参数传递的person对象。它们是两个不同的对象,而且我们通常需要丢弃person对象。

As with the persist method, the merge method is specified by JSR-220 to have certain semantics that we can rely upon:

persist方法一样,merge方法被JSR-220规定为具有某些语义,我们可以信赖。

  • if the entity is detached, it copies upon an existing persistent entity.
  • if the entity is transient, it copies upon a newly created persistent entity.
  • this operation cascades for all relations with cascade=MERGE or cascade=ALL mapping.
  • if the entity is persistent, then this method call doesn’t have an effect on it (but the cascading still takes place).

3.4. Update

3.4.更新

As with persist and save, the update method is an “original” Hibernate method. Its semantics differ in several key points:

persistsave一样,update方法是一个 “原始 “的Hibernate方法。它的语义在几个关键点上有所不同。

  • it acts upon a passed object (its return type is void). The update method transitions the passed object from a detached to persistent state.
  • this method throws an exception if we pass it a transient entity.

In the following example, we save the object, evict (detach) it from the context, and then change its name and call update. Notice that we don’t put the result of the update operation in a separate variable because the update takes place on the person object itself. Basically, we’re reattaching the existing entity instance to the persistence context, something the JPA specification doesn’t allow us to do:

在下面的例子中,我们保存对象,evict(脱离)它的上下文,然后改变它的名称并调用update。注意,我们没有把update操作的结果放在一个单独的变量中,因为update发生在person对象本身。基本上,我们将现有的实体实例重新连接到持久化上下文,这是JPA规范不允许我们做的。

Person person = new Person();
person.setName("John");
session.save(person);
session.evict(person);

person.setName("Mary");
session.update(person);

Trying to call update on a transient instance will result in an exception. The following won’t work:

试图在一个transient实例上调用update将导致一个异常。下面的方法不会起作用。

Person person = new Person();
person.setName("John");
session.update(person); // PersistenceException!

3.5. SaveOrUpdate

3.5.SaveOrUpdate

This method appears only in the Hibernate API and doesn’t have its standardized counterpart. Similar to update, we can also use it for reattaching instances.

这个方法只出现在Hibernate API中,并没有其标准化的对应方法。与update类似,我们也可以用它来重新连接实例。

Actually, the internal DefaultUpdateEventListener class that processes the update method is a subclass of DefaultSaveOrUpdateListener, just overriding some functionality. The main difference of the saveOrUpdate method is that it doesn’t throw an exception when applied to a transient instance, instead it makes this transient instance persistent. The following code will persist a newly created instance of Person:

实际上,处理update方法的内部DefaultUpdateEventListener类是DefaultSaveOrUpdateListener的一个子类,只是覆盖了一些功能。saveOrUpdate方法的主要区别是,当应用于transient实例时,它不会抛出一个异常,而是使这个transient实例persistent。下面的代码将持久化一个新创建的Person的实例。

Person person = new Person();
person.setName("John");
session.saveOrUpdate(person);

We can think of this method as a universal tool for making an object persistent regardless of its state, whether it’s transient or detached.

我们可以把这个方法看作是一个通用的工具,用于使一个对象持久化,不管它的状态如何,是瞬时还是分离

4. What to Use?

4.使用什么?

If we don’t have any special requirements, we should stick to the persist and merge methods because they’re standardized and will conform to the JPA specification.

如果我们没有任何特殊要求,我们应该坚持使用persistmerge方法,因为它们是标准化的,并将符合JPA规范。

They’re also portable in case we decide to switch to another persistence provider; however, they may sometimes seem not as useful as the “original” Hibernate methods, save, update, and saveOrUpdate.

它们也是可移植的,以备我们决定切换到另一个持久化提供者;然而,它们有时可能看起来不如 “原始 “的Hibernate方法,saveupdate,saveOrUpdate有用。

5. Conclusion

5.结论

In this article, we discussed the purpose of different Hibernate Session methods in relation to managing persistent entities in runtime. We learned how these methods transist entity instances through their lifecycles, and why some of these methods have duplicated functionality.

在这篇文章中,我们讨论了不同的Hibernate Session方法在运行时管理持久化实体方面的目的。我们了解了这些方法是如何在实体的生命周期中过渡到实体实例的,以及为什么其中一些方法有重复的功能。

The source code for the article is available on GitHub.

文章的源代码是在GitHub上提供的