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