在使用 Hibernate 實現實體類的自我關聯(一對多)時,開發者常遇到一個令人費解的現象:明明邏輯分離的兩個保存操作,卻在特定序列下產生交叉干擾。本文記錄一個真實案例,剖析其根因並給出解決方案。
實體 Catalog 通過 parentCatalog(多對一)與 childCatalogs(一對多,inverse="true"、cascade="save-update")形成樹狀結構:
<many-to-one name="parentCatalog" class="com.example.Catalog">
<column name="catalog_id"/>
</many-to-one>
<set name="childCatalogs" cascade="save-update" inverse="true" lazy="false">
<key><column name="catalog_id"/></key>
<one-to-many class="com.example.Catalog"/>
</set>
session.save(catalog)。session.save(parent) 與 session.save(child) 雙重保存。問題核心在於 Struts ActionForm 默認作用域為 session。當表單提交時,FormBean 實例存活於 Session 中,攜帶著上一次請求的屬性值(如 parentId、catalog 物件引用)。
B 方法執行後,FormBean 保留了父節點引用與子節點關聯。下一次請求進入 A 方法時,Struts 復用同一 FormBean 實例,導致「無父節點」的 A 方法實際上操作的是殘留在 FormBean 中的父節點物件,從而誤觸發級聯保存邏輯。
在 struts-config.xml 中將該 FormBean 的作用域顯式設為 request:
<form-bean name="catalogForm" type="com.example.CatalogForm" scope="request"/>
或在 Action 中手動 form.reset(mapping, request) 清理狀態。修改後,每次請求獲得全新 FormBean 實例,問題徹底消失。
request 作用域,除非確有跨請求共享表單數據的需求。