Object States in Hibernate’s Session – Hibernate的会话中的对象状态

最后修改: 2020年 10月 18日

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

1. Introduction

1.绪论

Hibernate is a convenient framework for managing persistent data, but understanding how it works internally can be tricky at times.

Hibernate是一个管理持久性数据的便捷框架,但理解它的内部工作方式有时会很棘手。

In this tutorial, we’ll learn about object states and how to move between them. We’ll also look at the problems we can encounter with detached entities and how to solve them.

在本教程中,我们将学习对象的状态以及如何在它们之间移动。我们还将看一下我们在分离实体方面可能遇到的问题以及如何解决这些问题。

2. Hibernate’s Session

2.Hibernate的会话

The Session interface is the main tool used to communicate with Hibernate. It provides an API enabling us to create, read, update, and delete persistent objects. The session has a simple lifecycle. We open it, perform some operations, and then close it.

Session接口是用于与Hibernate通信的主要工具。它提供了一个API,使我们能够创建、读取、更新和删除持久性对象。session有一个简单的生命周期。我们打开它,执行一些操作,然后关闭它。

When we operate on the objects during the session, they get attached to that session. The changes we make are detected and saved upon closing. After closing, Hibernate breaks the connections between the objects and the session.

当我们在session期间对对象进行操作时,它们被附加到该session。我们所做的改变在关闭时被检测并保存。关闭后,Hibernate会切断对象和会话之间的连接。

3. Object States

3.物体状态

In the context of Hibernate’s Session, objects can be in one of three possible states: transient, persistent, or detached.

在Hibernate的Session中,对象可以处于三种可能的状态之一:暂存、持久、分离。

3.1. Transient

3.1. 瞬态

An object we haven’t attached to any session is in the transient state. Since it was never persisted, it doesn’t have any representation in the database. Because no session is aware of it, it won’t be saved automatically.

一个我们没有附加到任何session的对象处于暂存状态。因为它从未被持久化,所以它在数据库中没有任何表示。因为没有session知道它,所以它不会被自动保存。

Let’s create a user object with the constructor and confirm that it isn’t managed by the session:

让我们用构造函数创建一个用户对象,并确认它不是由会话管理的。

Session session = openSession();
UserEntity userEntity = new UserEntity("John");
assertThat(session.contains(userEntity)).isFalse();

3.2. Persistent

3.2.持久性

An object that we’ve associated with a session is in the persistent state. We either saved it or read it from a persistence context, so it represents some row in the database.

我们与session关联的对象处于持久化状态。我们要么保存它,要么从持久化上下文中读取它,所以它代表了数据库中的某个行。

Let’s create an object and then use the persist method to make it persistent:

让我们创建一个对象,然后使用persist方法来使其持久化。

Session session = openSession();
UserEntity userEntity = new UserEntity("John");
session.persist(userEntity);
assertThat(session.contains(userEntity)).isTrue();

Alternatively, we may use the save method. The difference is that the persist method will just save an object, and the save method will additionally generate its identifier if that’s needed.

另外,我们也可以使用save方法。不同的是,persist方法将只是保存一个对象,而save方法将额外生成其标识符,如果需要的话。

3.3. Detached

3.3.独立的

When we close the session, all objects inside it become detached. Although they still represent rows in the database, they’re no longer managed by any session:

当我们关闭session时,它里面的所有对象都会被分离。尽管它们仍然代表数据库中的行,但它们不再被任何session管理:

session.persist(userEntity);
session.close();
assertThat(session.isOpen()).isFalse();
assertThatThrownBy(() -> session.contains(userEntity));

Next, we’ll learn how to save transient and detached entities.

接下来,我们将学习如何保存暂存和分离的实体。

4. Saving and Reattaching an Entity

4.保存和重新连接一个实体

4.1. Saving a Transient Entity

4.1.保存一个暂存的实体

Let’s create a new entity and save it to the database. When we first construct the object, it’ll be in the transient state.

让我们创建一个新的实体并将其保存到数据库中。当我们第一次构建这个对象时,它将处于暂存状态。

To persist our new entity, we’ll use the persist method:

为了persist我们的新实体,我们将使用persist方法。

UserEntity userEntity = new UserEntity("John");
session.persist(userEntity);

Now, we’ll create another object with the same identifier as the first one. This second object is transient because it’s not yet managed by any session, but we can’t make it persistent using the persist method. It’s already represented in the database, so it’s not really new in the context of the persistence layer.

现在,我们将创建另一个对象,其标识符与第一个对象相同。这第二个对象是暂存的,因为它还没有被任何session管理,但我们不能用persist方法使它持久化。它已经在数据库中表现出来了,所以在持久化层的背景下,它并不是真正的新对象。

Instead, we’ll use the merge method to update the database and make the object persistent:

相反,我们将使用merge方法来更新数据库并使该对象持久化

UserEntity onceAgainJohn = new UserEntity("John");
session.merge(onceAgainJohn);

4.2. Saving a Detached Entity

4.2.保存一个分离的实体

If we close the previous session, our objects will be in a detached state. Similarly to the previous example, they’re represented in the database but they aren’t currently managed by any session. We can make them persistent again using the merge method:

如果我们关闭前一个session,我们的对象将处于分离状态。与前面的例子类似,它们在数据库中被表示出来,但它们当前并没有被任何session管理。我们可以使用merge方法使它们再次持久化。

UserEntity userEntity = new UserEntity("John");
session.persist(userEntity);
session.close();
session.merge(userEntity);

5. Nested Entities

5.嵌套实体

Things get more complicated when we consider nested entities. Let’s say our user entity will also store information about his manager:

当我们考虑嵌套的实体时,事情就变得更加复杂。比方说,我们的用户实体也将存储关于他的经理的信息。

public class UserEntity {
    @Id
    private String name;

    @ManyToOne
    private UserEntity manager;
}

When we save this entity, we need to think not only about the state of the entity itself but also about the state of the nested entity. Let’s create a persistent user entity and then set its manager:

当我们保存这个实体时,我们不仅需要考虑实体本身的状态,还需要考虑嵌套实体的状态。让我们创建一个持久化的用户实体,然后设置其管理器。

UserEntity userEntity = new UserEntity("John");
session.persist(userEntity);
UserEntity manager = new UserEntity("Adam");
userEntity.setManager(manager);

If we try to update it now, we’ll get an exception:

如果我们现在尝试更新它,我们会得到一个异常。

assertThatThrownBy(() -> {
            session.saveOrUpdate(userEntity);
            transaction.commit();
});
java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.baeldung.states.UserEntity.manager -> com.baeldung.states.UserEntity

That’s happening because Hibernate doesn’t know what to do with the transient nested entity.

发生这种情况是因为Hibernate不知道该如何处理暂存的嵌套实体。

5.1. Persisting Nested Entities

5.1.持久的嵌套实体

One way to solve this problem is to explicitly persist nested entities:

解决这个问题的一个方法是显式地持久化嵌套实体。

UserEntity manager = new UserEntity("Adam");
session.persist(manager);
userEntity.setManager(manager);

Then, after committing the transaction, we’ll be able to retrieve the correctly saved entity:

然后,在提交事务后,我们就能检索到正确保存的实体。

transaction.commit();
session.close();

Session otherSession = openSession();
UserEntity savedUser = otherSession.get(UserEntity.class, "John");
assertThat(savedUser.getManager().getName()).isEqualTo("Adam");

5.2. Cascading Operations

5.2.级联操作

Transient nested entities can be persisted automatically if we configure the relationship’s cascade property correctly in the entity class:

如果我们在实体类中正确配置关系的cascade属性,瞬时嵌套的实体可以被自动持久化。

@ManyToOne(cascade = CascadeType.PERSIST)
private UserEntity manager;

Now when we persist the object, that operation will be cascaded to all nested entities:

现在当我们持久化对象时,该操作将被级联到所有嵌套的实体:

UserEntityWithCascade userEntity = new UserEntityWithCascade("John");
session.persist(userEntity);
UserEntityWithCascade manager = new UserEntityWithCascade("Adam");

userEntity.setManager(manager); // add transient manager to persistent user
session.saveOrUpdate(userEntity);
transaction.commit();
session.close();

Session otherSession = openSession();
UserEntityWithCascade savedUser = otherSession.get(UserEntityWithCascade.class, "John");
assertThat(savedUser.getManager().getName()).isEqualTo("Adam");

6. Summary

6.归纳总结

In this tutorial, we took a closer look at how the Hibernate Session works with respect to object state. We then inspected some problems it can create and how to solve them.

在本教程中,我们仔细研究了Hibernate Session在对象状态方面是如何工作的。然后我们检查了它可能产生的一些问题以及如何解决这些问题。

As always, the source code is available over on GitHub.

像往常一样,源代码可在GitHub上获得