今天在使用spring整合hibernate的架構中控制事務遇到資料更新未取到最新的資料,代碼結構如下:
注:此處調用查詢方法定義為query(),
調用更新方法定義為update(),
調用執行sql的方法定義為execute();
TableObject 對應表 dataTable;
彙總表對應 totalDataTable;
/**
*a方法循環調用b方法
*/
public void a(){ //事務控制層
List<String> priNumbers = new ArrayList<String>();
priNumbers.add("1");
priNumbers.add("2");
priNumbers.add("3");
//循環修改dataTable的值
for(String priNumber : priNumbers ){
b(priNumber);
}
//根據dataTable的最新值修改totalDataTable的彙總值
c();
}
/**
*b方法查詢對象并更新對象
*/
public void b(String priNumber){
//根據主鍵查詢資料庫表對象obj
TableObject obj = query(priNumber);
.
.
.//修改obj的值,此處省略
//更新obj值
update(obj);
}
/**
*c方法執行sql根據資料庫表dataTable中的最新資料所有值之和修改彙總表totalDataTable的資料
*/
public void c(){
execute(sql);
}
在執行上述代碼後,totalDataTable表中的資料總是取的是前兩次更新後資料和最後一次未更新的資料值的彙總值,原因是:
事務控制在a()方法層是以事務送出是在a()方法結束時再調用commit()方法,而hibernate機制是在事務送出時才調用flush()方法将持久化對象同步到資料庫,在此之前,該持久化對象是存在緩存中的,是以在b方法中執行update時資料庫中的資料是沒有同步的,是以我們會讀取到最後一次未更新的資料值,但是為什麼前兩次更新後的資料是資料庫的最新值呢?這是因為hibernate的緩存中隻能存在一個同一類型的持久化對象,在我們調用query()方法時,hibernate就會将上一次的儲存在緩存中的持久化對象flush到資料庫同步,并更新緩存中的持久化對象。
解決方法:因為我們要取到資料必須是資料庫最新的值,是以我們需要在調用hibernate的save()、update()、saveOrUpdate()方法後手動的調用一次flush()方法,使緩存資料與資料庫資料同步。
注:如果我們不添加事務,實際是不會出現以上問題的,因為未加事務時,hibernate預設事務送出是在dao層方法調用結束後調用flush()和commit()方法,而正是因為我們添加了事務,導緻hibernate調用flush()和commit()延遲到我們控制的事務結束時,這時執行的資料庫更新操作如果沒有調用flush()操作實際是存在緩存中而并沒有和資料庫同步的。