Hibernate Entity Lifecycle – Hibernate Entity Lifecycle

最后修改: 2018年 8月 18日

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

1. Overview

1.概述

Every Hibernate entity naturally has a lifecycle within the framework – it’s either in a transient, managed, detached or deleted state.

每个Hibernate实体在框架中都有一个生命周期–它要么处于暂存、管理、分离或删除状态。

Understanding these states on both conceptual and technical level is essential to be able to use Hibernate properly.

从概念和技术层面理解这些状态对于正确使用Hibernate至关重要。

To learn about various Hibernate methods that deal with entities, have a look at one of our previous tutorials.

要了解处理实体的各种Hibernate方法,请看我们以前的一个教程

2. Helper Methods

2.帮助者方法

Throughout this tutorial, we’ll consistently use several helper methods:

在本教程中,我们将持续使用几个辅助方法。

  • HibernateLifecycleUtil.getManagedEntities(session) – we’ll use it to get all managed entities from a Session’s internal store
  • DirtyDataInspector.getDirtyEntities() – we’re going to use this method to get a list of all entities that were marked as ‘dirty’
  • HibernateLifecycleUtil.queryCount(query) – a convenient method to do count(*) query against the embedded database

All of the above helper methods are statically imported for better readability. You can find their implementations in the GitHub project linked at the end of this article.

上述所有的辅助方法都是静态导入的,以提高可读性。你可以在本文末尾链接的GitHub项目中找到它们的实现。

3. It’s All About Persistence Context

3.这一切都与持久性背景有关

Before getting into the topic of entity lifecycle, first, we need to understand the persistence context.

在进入实体生命周期的话题之前,首先,我们需要了解持久化上下文

Simply put, the persistence context sits between client code and data store. It’s a staging area where persistent data is converted to entities, ready to be read and altered by client code.

简单地说,持久化上下文位于客户端代码和数据存储之间。它是一个暂存区,持久性数据被转换为实体,准备被客户端代码读取和改变。

Theoretically speaking, the persistence context is an implementation of the Unit of Work pattern. It keeps track of all loaded data, tracks changes of that data, and is responsible to eventually synchronize any changes back to the database at the end of the business transaction.

从理论上讲,持久化上下文工作单元模式的一个实现。它跟踪所有加载的数据,跟踪该数据的变化,并负责在业务事务结束时将任何变化最终同步回数据库。

JPA EntityManager and Hibernate’s Session are an implementation of the persistence context concept. Throughout this article, we’ll use Hibernate Session to represent persistence context.

JPA EntityManager和Hibernate的Session持久化上下文概念的一个实现。在本文中,我们将使用Hibernate的Session来表示持久化上下文

Hibernate entity lifecycle state explains how the entity is related to a persistence context, as we’ll see next.

Hibernate实体生命周期状态解释了实体与持久化上下文的关系,我们接下来会看到。

4. Managed Entity

4.被管理的实体

A managed entity is a representation of a database table row (although that row doesn’t have to exist in the database yet).

管理实体是数据库表行的代表(尽管该表行还不一定存在于数据库中)。

This is managed by the currently running Session, and every change made on it will be tracked and propagated to the database automatically.

这是由当前运行的Session管理的,其上的每一个变化都将被跟踪并自动传播到数据库中

The Session either loads the entity from the database or re-attaches a detached entity. We’ll discuss detached entities in section 5.

Session要么从数据库中加载实体,要么重新连接一个分离的实体。我们将在第5节讨论分离的实体。

Let’s observe some code to get clarification.

让我们观察一些代码以得到澄清。

Our sample application defines one entity, the FootballPlayer class. At startup, we’ll initialize the data store with some sample data:

我们的示例应用程序定义了一个实体,FootballPlayer类。在启动时,我们将用一些样本数据初始化数据存储。

+-------------------+-------+
| Name              |  ID   |
+-------------------+-------+
| Cristiano Ronaldo | 1     |
| Lionel Messi      | 2     |
| Gianluigi Buffon  | 3     |
+-------------------+-------+

Let’s say we want to change the name of Buffon to start with – we want to put in his full name Gianluigi Buffon instead of Gigi Buffon.

比方说,我们想首先改变布冯的名字–我们想把他的全名Gianluigi Buffon而不是Gigi Buffon。

First, we need to start our unit of work by obtaining a Session:

首先,我们需要通过获得一个Session:来开始我们的工作单元。

Session session = sessionFactory.openSession();

In a server environment, we may inject a Session to our code via a context-aware proxy. The principle remains the same: we need a Session to encapsulate the business transaction of our unit of work.

在服务器环境中,我们可以通过上下文感知代理向我们的代码注入一个Session。原理是一样的:我们需要一个Session来封装我们工作单元的业务事务。

Next, we’ll instruct our Session to load the data from the persistent store:

接下来,我们将指示我们的Session从持久化存储中加载数据。

assertThat(getManagedEntities(session)).isEmpty();

List<FootballPlayer> players = s.createQuery("from FootballPlayer").getResultList();

assertThat(getManagedEntities(session)).size().isEqualTo(3);

When we first obtain a Session, its persistent context store is empty, as shown by our first assert statement.

当我们第一次获得一个Session时,它的持久化上下文存储是空的,正如我们的第一个assert语句所示。

Next, we’re executing a query which retrieves data from the database, creates entity representation of the data, and finally returns the entity for us to use.

接下来,我们要执行一个查询,从数据库中检索数据,创建数据的实体表示,并最终返回实体供我们使用。

Internally, the Session keeps track of all entities it loads in the persistent context store. In our case, the Session’s internal store will contain 3 entities after the query.

在内部,Session在持久化上下文存储中保持对其加载的所有实体的跟踪。在我们的案例中,Session的内部存储将在查询后包含3个实体。

Now let’s change Gigi’s name:

现在让我们改变一下吉吉的名字。

Transaction transaction = session.getTransaction();
transaction.begin();

FootballPlayer gigiBuffon = players.stream()
  .filter(p -> p.getId() == 3)
  .findFirst()
  .get();

gigiBuffon.setName("Gianluigi Buffon");
transaction.commit();

assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Gianluigi Buffon");

4.1. How Does It Work?

4.1.它是如何工作的?

On call to transaction commit() or flush(), the Session will find any dirty entities from its tracking list and synchronize the state to the database.

在调用事务commit()flush()时,Session将从其跟踪列表中找到任何dirty实体并将状态同步到数据库。

Notice that we didn’t need to call any method to notify Session that we changed something in our entity – since it’s a managed entity, all changes are propagated to the database automatically.

注意,我们不需要调用任何方法来通知Session我们在实体中改变了什么–因为它是一个被管理的实体,所有的改变都会自动传播到数据库。

A managed entity is always a persistent entity – it must have a database identifier, even though the database row representation is not yet created i.e. the INSERT statement is pending the end of the unit of work.

一个被管理的实体总是一个持久的实体–它必须有一个数据库标识符,即使数据库行的表示还没有被创建,即INSERT语句还在等待工作单元的结束。

See the chapter about transient entities below.

见下面关于暂存实体的章节。

5. Detached Entity

5.分离的实体

detached entity is just an ordinary entity POJO whose identity value corresponds to a database row. The difference from a managed entity is that it’s not tracked anymore by any persistence context.

一个独立实体只是一个普通的实体POJO,其身份值对应于数据库行。与托管实体的区别在于,它不再被任何持久化上下文所跟踪。

An entity can become detached when the Session used to load it was closed, or when we call Session.evict(entity) or Session.clear().

当用于加载实体的Session被关闭时,或者当我们调用Session.evict(entity)Session.clear()时,一个实体就会被分离。

Let’s see it in the code:

让我们在代码中看到它。

FootballPlayer cr7 = session.get(FootballPlayer.class, 1L);

assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(getManagedEntities(session).get(0).getId()).isEqualTo(cr7.getId());

session.evict(cr7);

assertThat(getManagedEntities(session)).size().isEqualTo(0);

Our persistence context will not track the changes in detached entities:

我们的持久化上下文将不会跟踪分离实体的变化。

cr7.setName("CR7");
transaction.commit();

assertThat(getDirtyEntities()).isEmpty();

Session.merge(entity)/Session.update(entity) can (re)attach a session:

Session.merge(entity)/Session.update(entity)可以(重新)附加一个会话

FootballPlayer messi = session.get(FootballPlayer.class, 2L);

session.evict(messi);
messi.setName("Leo Messi");
transaction.commit();

assertThat(getDirtyEntities()).isEmpty();

transaction = startTransaction(session);
session.update(messi);
transaction.commit();

assertThat(getDirtyEntities()).size().isEqualTo(1);
assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Leo Messi");

For reference on both Session.merge() and Session.update() see here.

关于Session.merge()Session.update()的参考,请参见这里

5.1. The Identity Field Is All That Matters

5.1.身份领域是最重要的

Let’s have a look at the following logic:

让我们看一下下面的逻辑。

FootballPlayer gigi = new FootballPlayer();
gigi.setId(3);
gigi.setName("Gigi the Legend");
session.update(gigi);

In the example above, we’ve instantiated an entity in the usual way via its constructor. We’ve populated the fields with values and we’ve set the identity to 3, which corresponds to the identity of persistent data that belongs to Gigi Buffon. Calling update() has exactly the same effect as if we’ve loaded the entity from another persistence context.

在上面的例子中,我们以通常的方式通过其构造函数实例化了一个实体。我们用值填充了字段,并将身份设置为3,这与属于Gigi Buffon的持久化数据的身份相对应。调用update()的效果与我们从另一个持久化上下文加载实体完全相同。

In fact, Session doesn’t distinguish where a re-attached entity originated from.

事实上,Session并不区分一个重新连接的实体来自哪里。

It’s quite a common scenario in web applications to construct detached entities from HTML form values.

在Web应用程序中,从HTML表单值中构建分离的实体是很常见的情况。

As far as Session is concerned, a detached entity is just a plain entity whose identity value corresponds to persistent data.

Session而言,分离的实体只是一个普通的实体,其身份值与持久性数据相对应。

Be aware that the example above just serves a demo purpose. and we need to know exactly what we’re doing. Otherwise, we could end up with null values across our entity if we just set the value on the field we want to update, leaving the rest untouched (so, effectively null).

请注意,上面的例子只是一个演示的目的。我们需要确切地知道我们在做什么。否则,如果我们只是在我们想要更新的字段上设置值,而不触动其他字段(所以,实际上是空值),我们可能会在整个实体中出现空值。

6. Transient Entity

6.短暂的实体

A transient entity is simply an entity object that has no representation in the persistent store and is not managed by any Session.

暂存实体只是一个实体对象,它在持久化存储中没有任何表示,也不被任何Session管理。

A typical example of a transient entity would be instantiating a new entity via its constructor.

瞬时实体的一个典型例子是通过其构造函数来实例化一个新实体。

To make a transient entity persistent, we need to call Session.save(entity) or Session.saveOrUpdate(entity):

为了使一个暂存的实体持久化,我们需要调用Session.save(entity)Session.saveOrUpdate(entity):

FootballPlayer neymar = new FootballPlayer();
neymar.setName("Neymar");
session.save(neymar);

assertThat(getManagedEntities(session)).size().isEqualTo(1);
assertThat(neymar.getId()).isNotNull();

int count = queryCount("select count(*) from Football_Player where name='Neymar'");

assertThat(count).isEqualTo(0);

transaction.commit();
count = queryCount("select count(*) from Football_Player where name='Neymar'");

assertThat(count).isEqualTo(1);

As soon as we execute Session.save(entity), the entity is assigned an identity value and becomes managed by the Session. However, it might not yet be available in the database as the INSERT operation might be delayed until the end of the unit of work.

只要我们执行Session.save(entity),该实体就会被分配一个身份值,并成为Session的管理对象。然而,由于INSERT操作可能被推迟到工作单元结束时,它可能还不能在数据库中使用。

7. Deleted Entity

7.被删除的实体

An entity is in a deleted (removed) state if Session.delete(entity) has been called, and the Session has marked the entity for deletion. The DELETE command itself might be issued at the end of the unit of work.

如果Session.delete(entity)被调用,并且Session已经标记该实体为删除状态,那么该实体就处于删除(移除)状态。DELETE命令本身可能会在工作单元的末尾发出。

Let’s see it in the following code:

让我们在下面的代码中看到它。

session.delete(neymar);

assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);

However, notice that the entity stays in the persistent context store until the end of the unit of work.

然而,请注意,该实体一直留在持久化上下文存储中,直到工作单元结束。

8. Conclusion

8.结论

The concept of persistence context is central to understanding the lifecycle of Hibernate entities. We’ve clarified the lifecycle by looking into the code examples demonstrating each status.

持久化上下文的概念是理解Hibernate实体生命周期的核心。我们通过研究演示每种状态的代码实例来阐明生命周期。

As usual, the code used in this article can be found over on GitHub.

像往常一样,本文中使用的代码可以在GitHub上找到over