Hibernate中Session的對象狀態

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在對象狀態方面的工作方式。然後,我們檢查了它可能產生的一些問題以及如何解決它們。