在前幾篇筆記中,我們已經熟悉了 Hibernate 的基本配置與 CRUD 操作。本篇將重點探討 saveOrUpdate() 這個極具實用價值的方法,它能根據對象的狀態自動決定執行「新增」還是「更新」操作,極大地簡化了業務邏輯代碼。
要讓 saveOrUpdate() 正確運作,映射文件(User.hbm.xml)中的 <id> 元素配置至關重要,特別是 unsaved-value 屬性:
<id name="id" column="id" type="java.lang.Integer" unsaved-value="null">
<generator class="native" />
</id>
unsaved-value="null" 表示:當 ID 為 null 時視為「瞬時對象」(執行 save),否則視為「脫管對象」(執行 update)。這是預設行為,也是最常用的策略。
以下測試代碼展示了兩種典型場景:
// 場景一:指定 ID 為 2,對象已存在於數據庫 → 執行 UPDATE
User user = new User();
user.setId(2); // 關鍵:設置非 null ID
user.setName("chenyan");
user.setAge(24);
session.saveOrUpdate(user); // 執行 update user set name=?, age=? where id=?
// 場景二:不設置 ID,ID 為 null → 執行 INSERT
user = new User();
user.setName("yangye");
user.setAge(23);
session.saveOrUpdate(user); // 執行 insert into user (name, age) values (?, ?)
控制台輸出清晰地印證了上述邏輯:
Hibernate: update user set name=?, age=? where id=?
========================================
Hibernate: insert into user (name, age) values (?, ?)
========================================
id name/age
1 shenbin/28
2 chenyan/24
3 yangye/23
第一次調用發出 UPDATE 語句,修改了 ID 為 2 的記錄;第二次調用發出 INSERT 語句,新增了一條 ID 為 3 的記錄。
unsaved-value 可選值詳解| 值 | 行為 |
|---|---|
null | ID 為 null 時執行 save(預設) |
any | 總是執行 save |
none | 總是執行 update |
valid | ID 為 null 或指定值時執行 save |
在同一個 Session 中,若先查詢出對象,再嘗試用 saveOrUpdate() 處理一個相同 ID 的新對象實例,會拋出異常:
org.hibernate.NonUniqueObjectException:
a different object with the same identifier value was already associated with the session: [com.hb3.pack_01.model.User#2]
原因:Session 緩存中已存在該 ID 的對象,Hibernate 禁止兩個不同實例對應同一個資料庫標識符。
解決方案:
session.merge() 替代(會將脫管對象狀態合併到持久化對象中)evict() 對象,使其脫管saveOrUpdate() 是 Hibernate 提供的「智能持久化」方法,配合正確的 unsaved-value 策略,能自動判斷對象生命週期,減少手動區分新增/更新的樣板代碼。但需注意 Session 緩存機制,避免同一 Session 中出現相同標識符的多個實例。