Hibernate的SQL執行順序引發的血案
阿新 • • 發佈:2019-01-03
問題起源於程式碼中對某個物件的處理邏輯,當發現該物件的某些屬性變化時,先將原資料刪除,再插入一個新物件,由於使用了Spring+Hibernate的組合,很容易寫出如下程式碼:
//從資料庫中讀取原資料
Role entity = hibernateTemplate.load(ID);
//將資料複製到新物件上
Role newEntity = new Role();
BeanCopier.copy(entity, newEntity);
//刪除原資料
hibernateTemplate.delete(entity);
//儲存新資料
hibernateTemplate.save(entity);
然而這段程式碼在執行時卻失敗了,程式丟擲:DataIntegrityViolationException異常,日誌顯示如下錯誤:
SQL Error: 1062, SQLState: 23000
Duplicate entry 'demo' for key 'IDX_ROLE_NAME'
即資料插入資料庫時失敗了,原因是觸發了唯一性約束。經反覆核實,資料庫中資料沒有問題,不是其他資料導致的,從程式碼邏輯上講,這段程式碼先刪後插也沒有任何問題,在一個事務當中也沒有什麼髒讀的問題,
這就搞笑了,問題到底出在哪呢???開啟Hibernate的SQL輸出,還原最真實的SQL語句,終於找到了答案,輸入日誌如下:
Hibernate:
/* insert com.litt.cidp.system.po.Role
*/ insert
into
role
(ROLE_NAME, STATUS, REMARK)
values
(?, ?, ?)
Hibernate:
/* update
com.litt.cidp.system.po.Role */ update
role
set
ROLE_NAME=?,
STATUS=?,
REMARK=?
where
ROLE_ID=?
Hibernate:
/* delete com.litt.cidp.system.po.Role */ delete
from
role
where
ROLE_ID=?
Hibernate在最終執行SQL語句時,居然是按INSERT, UPDATE, DELETE的順序執行的,而非按照程式碼順序執行!What the F!查閱了相關資料,說是Hibernate為了效能優化,物件操作都是放在快取裡而沒有立即執行資料庫操作直到commit操作,為的是利用batch操作提高資料庫操作效能;老天,那業務邏輯怎麼辦?同理,在一個事務中混用Hibernate和JDBC操作,將導致資料不一致問題。
解決方案:在需要同步的地方(即按照INSERT, UPDATE, DELETE順序有可能產生問題的時候;混用JDBC操作的時候),執行session.flush()方案,該方法將快取中的資料請求立即轉換為資料庫操作並執行。