Hibernate 最核心概念是持久化物件 (PO) 的狀態管理。理解 update() 與 saveOrUpdate() 的差異,關鍵在於掌握 PO 的三種狀態與跨 Session 情境。
| 狀態 | 說明 | 生命週期管理 |
|---|---|---|
| 1. 瞬時態 | 新 new 出來的物件,未持久化 | JVM (記憶體) |
| 2. 持久態 | 已關聯 Session,對應資料庫記錄 | 資料庫 (透過 Session) |
| 3. 脫管態 | 曾持久化,但 Session 已關閉/清除 | JVM (但保留持久化身份標識) |
Hibernate 強大之處: 脫管態物件可進入新 Session 繼續狀態管理 (JDO 1.x 做不到),這需要 update() 或 saveOrUpdate() 協助 — Reference 稱為「requires a slightly different programming model」。
只有「跨 Session」且「在 Session 外修改了屬性」時才需要。
Foo foo = sess.load(Foo.class, id);
foo.setXXX(xxx);
sess.flush(); // Hibernate 自動偵測變更發送 UPDATE SQL
sess.commit();
加上 sess.update(foo) 雖不報錯但多餘。
// Session 1
Cat cat = (Cat) firstSession.load(Cat.class, catId);
Cat potentialMate = new Cat();
firstSession.save(potentialMate);
// 應用層 (Session 外)
cat.setMate(potentialMate); // 修改關聯
// Session 2
secondSession.update(cat); // 必須顯式 update
secondSession.update(mate);
cat、mate 在 Session 1 關閉後變為脫管態。Session 2 不知道 cat.setMate() 這個 Session 外修改,**必須呼叫 update() 明確告知「這個物件變了,請發送 UPDATE SQL」**。
DAO 封裝持久化邏輯,業務層調用 addMate(Cat cat, Mate mate)。業務層不知道傳入的 cat 是:
save())update())若 DAO 寫死 session.update(cat),傳入新物件會拋異常;寫死 save(),傳入脫管物件會重複插入。
public void addMate(Cat cat, Mate mate) {
Session session = ...;
Transaction tx = ...;
session.saveOrUpdate(cat); // 自動判斷:瞬時態→save,脫管態→update
cat.addMate(mate);
tx.commit();
session.close();
}
判斷依據: unsaved-value 設定 (通常是 id 屬性為 null/0 視為瞬時態)。
// 業務層完全不用關心 cat 狀態
dao.addMate(cat, mate);
| 方法 | 適用場景 | 行為 | 風險 |
|---|---|---|---|
update() | 確定是脫管態物件,需同步修改 | 強制執行 UPDATE SQL | 傳入瞬時態拋異常 |
saveOrUpdate() | 物件狀態未知 (DAO 介面、通用方法) | 根據 unsaved-value 自動判斷 save 或 update | 極少 (需正確配置 unsaved-value) |
saveOrUpdate(),封裝狀態判斷邏輯,讓上層無感知update() 語義更清晰掌握跨 Session 狀態管理,是從「會用 Hibernate」進階到「善用 Hibernate」的關鍵分水嶺。