1.简介
Hibernate是用于管理持久性数据的便捷框架,但是有时了解其内部工作方式可能很棘手。
在本教程中,我们将学习对象状态以及如何在它们之间移动。我们还将研究分离实体可能遇到的问题以及如何解决它们。
2. Hibernate的Session
Session
接口是用于与Hibernate通信的主要工具。它提供了一个API,使我们能够创建,读取,更新和删除持久对象。该session
具有简单的生命周期。我们打开它,执行一些操作,然后关闭它。
session
期间我们对对象进行操作时,它们会附加到该session
。我们所做的更改将在关闭时被检测并保存。关闭后,Hibernate断开对象与会话之间的连接。
3.对象状态
在Hibernate的Session
上下文中,对象可以处于三种可能的状态之一:临时,持久或分离。
3.1 短暂Transient
我们尚未附加到任何session
的对象处于过渡状态。由于它从未被持久保存,因此它在数据库中没有任何表示形式。由于没有session
知道它,因此不会自动保存它。
让我们用构造函数创建一个用户对象,并确认它不是由会话管理的:
Session session = openSession();
UserEntity userEntity = new UserEntity("John");
assertThat(session.contains(userEntity)).isFalse();
3.2 Persistent持久
session
相关联的对象处于持久状态。我们要么保存它,要么从持久性上下文中读取它,因此它表示数据库中的某些行。
让我们创建一个对象,然后使用persist
方法将其持久化:
Session session = openSession();
UserEntity userEntity = new UserEntity("John");
session.persist(userEntity);
assertThat(session.contains(userEntity)).isTrue();
或者,我们可以使用save
方法。区别在于, persist
方法将只保存一个对象,而save
方法将另外生成其标识符(如果需要)。
3.3 Detached分离
当我们关闭session
,其中的所有对像都将分离。尽管它们仍然代表数据库中的行,但是它们不再由任何session
管理:
session.persist(userEntity);
session.close();
assertThat(session.isOpen()).isFalse();
assertThatThrownBy(() -> session.contains(userEntity));
接下来,我们将学习如何保存临时实体和分离实体。
4.保存并重新连接实体
4.1 保存瞬态实体
让我们创建一个新实体并将其保存到数据库中。当我们第一次构造对象时,它将处于过渡状态。
为了persist
我们的新实体,我们将使用persist
方法:
UserEntity userEntity = new UserEntity("John");
session.persist(userEntity);
现在,我们将创建另一个标识符,该标识符与第一个相同。第二个对象是临时的,因为它尚未由任何session
管理,但是我们不能使用persist
方法将其持久化。它已经在数据库中表示出来,因此在持久层的上下文中并不是真正的新事物。
相反,我们将使用merge
方法更新数据库并使对象持久化:
UserEntity onceAgainJohn = new UserEntity("John");
session.merge(onceAgainJohn);
4.2。保存分离Detached的实体
如果关闭上一个session
,则对象将处于分离状态。与前面的示例类似,它们在数据库中表示,但是当前不受任何session
管理。 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;
}
保存此实体时,我们不仅需要考虑实体本身的状态,还需要考虑嵌套实体的状态。让我们创建一个持久的用户实体,然后设置其管理器:
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 : com.baeldung.states.UserEntity.manager -> com.baeldung.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
在对象状态方面的工作方式。然后,我们检查了它可能产生的一些问题以及如何解决它们。
0 评论