1. 程式人生 > >Hibernate的SQL執行順序引發的血案

Hibernate的SQL執行順序引發的血案

問題起源於程式碼中對某個物件的處理邏輯,當發現該物件的某些屬性變化時,先將原資料刪除,再插入一個新物件,由於使用了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()方案,該方法將快取中的資料請求立即轉換為資料庫操作並執行。