Hibernate会话中的对象状态

2023/05/18

1. 简介

Hibernate是一个用于管理持久数据的便捷框架,但有时很难理解它的内部工作原理。

在本教程中,我们将了解对象状态以及如何在它们之间移动。我们还将研究分离实体可能遇到的问题以及如何解决这些问题。

2. Hibernate的Session

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

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

3. 对象状态

在Hibernate的Session上下文中,对象可以处于三种可能的状态之一:瞬时、持久或分离。

3.1 瞬时态

我们没有附加到任何会话的对象处于瞬时态。由于它从未被持久化,因此它在数据库中没有任何表示。由于没有会话知道它,因此它不会自动保存。

让我们使用构造函数创建一个用户对象,并断言它不是由会话管理的:

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

3.2 持久态

我们与会话关联的对象处于持久状态。我们要么保存它,要么从持久性上下文中读取它,因此它代表数据库中的某一行数据。

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

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

或者,我们可以使用save方法。不同之处在于persist方法只会保存一个对象,而save方法会在需要时额外生成它的id。

3.3 分离态

当我们关闭会话时,其中的所有对象都会分离。尽管它们仍然代表数据库中的某一行,但它们不再由任何会话管理

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

接下来,我们介绍如何保存瞬态实体和分离实体。

4. 保存和重新附加实体

4.1 保存瞬态实体

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

为了持久化我们的新实体,我们将使用persist方法:

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

现在,我们将创建另一个与第一个对象具有相同标识符的对象。第二个对象是瞬态的,因为它还没有被任何会话管理,但我们不能使用persist方法使它持久化。它已经出现在数据库中,所以在持久层的上下文中它并不是真正的新事物。

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

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

4.2 保存分离的实体

如果我们关闭上一个会话,我们的对象将处于分离状态。与前面的示例类似,它们在数据库中表示,但当前不受任何会话管理。我们可以使用merge方法使它们再次持久化:

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

5. 嵌套实体

当我们考虑嵌套实体时,事情变得更加复杂。假设我们的用户实体还将存储有关其经理的信息:

public class UserEntity {
    @Id
    private String name;

    @ManyToOne
    private UserEntity manager;
}

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

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

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

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

发生这种情况是因为Hibernate不知道如何处理瞬态嵌套实体。

5.1 持久化嵌套实体

解决此问题的一种方法是显式持久化嵌套实体:

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

然后,在提交事务后,我们将能够检索正确保存的实体:

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

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

5.2 级联操作

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

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

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

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. 总结

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

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章