Hibernate 關係映射中,inverse 與 cascade 是最容易混淆、配置錯誤導致數據不一致的兩個屬性。本文從原理、SQL 行為、作用時機、組合矩陣四個維度徹底釐清,給出生產環境標準配置建議。
| 屬性 | 核心含義 | 作用對象 | 觸發時機 |
|---|---|---|---|
inverse | 「誰維護關係」——關係端控制權 | 集合層級(<set>, <bag>) | Session flush 時(提交前) |
cascade | 「誰的生命週期牽動誰」——實體層級操作傳播 | 單個實體元素 | 調用 Session 方法時(save/delete/update 即時) |
inverse="false"(默認):**雙向維護**,兩端操作都會同步關係表/外鍵列。inverse="true":放棄維護權,由關係另一端負責同步。場景:School (1) —<set>— (N) Student,外鍵在 Student 表 school_id。
| 操作 | inverse=false (默認) | inverse=true |
|---|---|---|
| 新增 School 並關聯 Students | INSERT School → INSERT Students → UPDATE Students SET school_id=? | INSERT School → INSERT Students (無 UPDATE) |
| 從 School 移除 Student | UPDATE Student SET school_id=NULL | 無操作(需在 Student 端維護) |
| 刪除 School (cascade=delete) | DELETE School → UPDATE Students SET school_id=NULL → DELETE Students | DELETE School → DELETE Students (無 UPDATE) |
結論:一對多**強烈建議集合端設置 inverse="true"**,由多端(持有外鍵列)維護關係,避免額外 UPDATE,性能更優、語義更清晰。
關係表獨立存在,inverse="true" 端不操作關係表,另一端負責 INSERT/DELETE 關係表記錄。兩端效能相近,**任選一端為 true 即可**,不可雙真(關係表無人維護)或雙假(重複維護導致主鍵衝突/死鎖)。
none(默認):無級聯。save-update:save()/update()/saveOrUpdate() 級聯。delete:delete() 級聯刪除關聯對象。delete-orphan:從集合移除孤兒對象時刪除(僅一對多)。all:包含以上所有。all-delete-orphan:all + delete-orphan。evict/lock/refresh/replicate:對應同名 Session 方法。調用 session.save(school) 瞬間,級聯 save(student) 立即執行,不等待 flush。這意味著 cascade 操作在同一事務中,但可能先於集合的 inverse 邏輯。
| 維度 | inverse | cascade |
|---|---|---|
| 作用粒度 | 集合整體 | 集合中每個元素 |
| 空集合行為 | 執行 UPDATE/DELETE 關係表 | 無操作(無元素可級聯) |
| new School + 空 set | inverse=false 會 UPDATE 關係表 | 無級聯操作 |
| 修改集合部分元素 | Diff 算法:僅對變化元素執行 SQL | 遍歷所有元素執行級聯(內部判斷髒數據) |
| 執行順序 | Flush 階段(最後) | Session 方法調用時(較早) |
| 適用關係類型 | <set> + one-to-many/many-to-many | 所有關係類型 |
<!-- School.hbm.xml -->
<set name="students" inverse="true" cascade="save-update, delete-orphan">
<key column="school_id" not-null="true"/>
<one-to-many class="Student"/>
</set>
<!-- Student.hbm.xml -->
<many-to-one name="school" class="School" column="school_id"
not-null="true" cascade="save-update"/>
<!-- Student -->
<set name="courses" table="student_course" inverse="false" cascade="save-update">
<key column="student_id"/>
<many-to-many class="Course" column="course_id"/>
</set>
<!-- Course -->
<set name="students" table="student_course" inverse="true" cascade="save-update">
<key column="course_id"/>
<many-to-many class="Student" column="student_id"/>
</set>
<one-to-one name="profile" class="UserProfile" constrained="true" cascade="all-delete-orphan"/>
constrained="true" 暗示外鍵約束,主表端通常 cascade="all-delete-orphan",從表端不配置 cascade。cascade="save-update" 即可)。delete-orphan 要求被移除對象無其他引用。正確配置 inverse 與 cascade,是 Hibernate 從「能跑通」進入「高性能、數據一致、可維護」的關鍵分水嶺。建議在項目啟動時建立《Hibernate 映射規範文檔》,統一團隊配置風格,Code Review 重點檢查這兩個屬性。