| 狀態 | 別名 | 核心特徵 | 典型場景 |
|---|---|---|---|
| Transient | 瞬時態 / 臨時態 | 新建對象,無 ID,未關聯 Session,不在資料庫 | new User() |
| Persistent | 持久態 | 關聯 Session,有 ID,對應資料庫記錄,修改自動同步 | session.save()/get()/load() |
| Detached | 托管態 / 游離態 | 曾持久化,但 Session 關閉/清理/evict,有 ID 但不自動同步 | session.close()/evict()/clear() |
new() save()/persist()
+-----------+ +--------------+
| Transient | -----------------> | Persistent |
+-----------+ +--------------+
^ | | ^
| | delete() | | close()/evict()/clear()
| | | |
| +----------------------------+ |
| update()/merge() |
| |
| (再次關聯 Session) |
+----------------------------------+
Detached
| 從 \ 到 | Persistent | Detached | Transient |
|---|---|---|---|
| Transient | save(), persist(), saveOrUpdate() | — | — |
| Persistent | — | close(), evict(), clear() | delete() |
| Detached | update(), merge(), lock(), replicate() | — | delete() (需先關聯) |
// 持久態:修改即生效(髒檢查自動同步)
User u = session.get(User.class, 1L);
u.setName("New Name"); // 無需調用 update,事務提交時自動生成 UPDATE SQL
// 托管態:修改不生效,需重新關聯
session.close(); // u 變為 Detached
u.setName("Another Name"); // 內存中改了,但資料庫不變
session2.update(u); // 重新關聯,事務提交時同步
現象:Web 層接收前端表單綁定到實體,Service 層直接修改字段卻未調用 merge(),資料庫未更新。
修正:User managed = session.merge(detachedUser); managed.setName(...);
merge() 會複製一份持久化副本,若同一 Session 已加載該 ID 對象,會拋 NonUniqueObjectException。
對策:先 session.contains(entity) 判斷,或統一用 saveOrUpdate()(Hibernate 原生)。
CascadeType.ALL 導致大量關聯對象逐個持久化/合併。
優化:批次處理、手動控制級聯、使用 DTO 傳輸而非實體跨層。
@Entity
public class Order {
@Id @GeneratedValue
private Long id;
@OneToMany(mappedBy = "order", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this); // 雙向關聯維護
}
}
// 使用:僅持久化 Order,items 自動級聯持久(瞬時 -> 持久)
session.persist(order);
掌握三態轉換是 Hibernate 高效開發的基石:瞬時靠 save 入庫,持久靠 Session 管,托管靠 merge 回歸。