記錄我在使用spring,hibernate的時候遇到的session,和事務(wù)管理的問題. spring用一個OpenSessionInView的filter來處理session was closed的問題.這個大家并不陌生. 我們項目當(dāng)中的dao層有一個baseDao. 封裝了一系列對持久化對象的操作方法.C,R,U,D 條件查詢.分頁查詢.等等.而且baseDao當(dāng)中的所有的find方法都是readOnly的,get和load直接調(diào)用的hibernateTemplate的get和load.當(dāng)然service層當(dāng)中的事務(wù)管理也是使用spring的那個事務(wù)模板.
以上配置都OK. 我遇到的問題有幾個, 如下: 問題1,需要實現(xiàn)這樣一個業(yè)務(wù)邏輯: 先把對象find出來.然后改變某個屬性.然后在update. 在service當(dāng)中就會寫這樣一個方法.changeOrder.在changeOrder當(dāng)中先用dao的find.然后在用dao的update.理論上是可行的.因為service的事務(wù)都是被spring的事務(wù)模板托管.而且changeOrder得到的connection是可寫的.(因為spring的事務(wù)模板根據(jù)對方法名的匹配來判斷獲得得connection類型).但是由于baseDao.當(dāng)中的所有find方法都是 setReadyOnly(true). 這樣.當(dāng)在service執(zhí)行任何find的時候.baseDao將強行把connection改為只讀的.接下來在一個事務(wù)當(dāng)中.任何update 和save動作都不能完成了.但是直接執(zhí)行hibernateTemplate的get和load卻不會出現(xiàn)這個問題.因為這個connection的屬性是由spring的openSessionInView來處理的.在request一過來的時候spring會綁定一個session.到request.直至request結(jié)束.(在這段過程當(dāng)中如果不認(rèn)為改變connection的readOnly的屬性.這個connection將會從請求一開始到結(jié)束都是可以寫入的.) 解決的辦法就是在自己的dao當(dāng)中將find方法重載.將readOnly改為false. 問題2, 有兩個方法.一個是get對象.一個是find對象.同樣也是直接調(diào)用baseDao的get和find方法. 當(dāng)我對一個對象進行編輯操作的時候發(fā)現(xiàn)service當(dāng)中的update是有效的. 但是我find出來的對象.在利用service當(dāng)中的update來更新卻發(fā)現(xiàn)沒有任何異常.但是就是更新不了對象. 后來才明白.get方法當(dāng)中是沒有對connection進行任何readOnly相關(guān)的操作.但是baseDao當(dāng)中卻設(shè)置了只讀..這個時候又有一些疑問了. action并沒有進行事務(wù)管理.當(dāng)先調(diào)用service的find方法(也就是調(diào)用了baseDao中的find方法).這一個事務(wù)已經(jīng)提交了.然后在繼續(xù)調(diào)用service的update.為什么會更新不了對象? 原因就是在于OpenSessionInView.綁定的一個session對象在這一次的request當(dāng)中.所以.從一次request.開始到結(jié)束.這個request僅僅會操作當(dāng)前的一個session對象. 盡管在action當(dāng)中連續(xù)調(diào)用的兩次service方法都有兩個不同的事務(wù)范圍.在一整個請求當(dāng)中還是只存在一個session對象. 所以第一個service的find方法執(zhí)行完畢之后已經(jīng)將當(dāng)前request范圍內(nèi)的session改成了readOnly.以后的所有的service操作都是只讀的.后面的service一些save或者update方法都會失效.....這就是OpenSessionInView和事務(wù)之間的微妙關(guān)系. 問題3,在ajax異步調(diào)用當(dāng)中.經(jīng)常也會出現(xiàn)這一系列的問題.其實原理大都是一樣.因為ajax后來也是一個以.dwr結(jié)尾的請求.在OpenSessionInView當(dāng)中加入一個filtermapping 為 .dwr 這樣它會攔截所有的.dwr請求.在所有的ajax操作當(dāng)中會綁定一個session對象. 總結(jié)一下: OpenSessionInView是一個filter.它會為每一個request綁定一個session.任何接下來在這一次請求當(dāng)中所有的hibernate操作.都是基于當(dāng)前請求的這個session的.任何service或者dao把當(dāng)前的session對象改為了readOnly后.接下來所有save or update操作將進行不了.盡管他們不是在一個service方法(不是在同一個事務(wù)當(dāng)中進行). BTW.service在很大程度上是可以和dao層混合到一起.這樣可以節(jié)約很多代碼.但是.也會帶來維護的時候非常的負(fù)責(zé).并且麻煩.特別是readOnly 和session was closed問題.會另你非常的沮喪. dao實際上是不需要用transaction來管理的.真正需要事務(wù)的是項目當(dāng)中的service層.理解才是最重要的.用好OpenSessionInView會給項目帶來極大的方便. |
|