spring Transaction Manager和hibernate session 吐血經驗談

記錄我在使用spring,hibernate的時候遇到的session,和事務管理的問題.
spring用一個OpenSessionInView的filter來處理session was closed的問題.這個大家並不陌生.
我們項目當中的dao層有一個baseDao. 封裝了一系列對持久化對象的操作方法.C,R,U,D 條件查詢.分頁查詢.等等.而且baseDao當中的所有的find方法都是readOnly的,get和load直接調用的 hibernateTemplate的get和load.當然service層當中的事務管理也是使用spring的那個事務模板.


<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory">
			<ref local="sessionFactory" />
		</property>
	</bean>
	
	 
	<bean id="txProxyTemplate" lazy-init="true" abstract="true"
		class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager">
			<ref bean="transactionManager" />
		</property>
		<property name="transactionAttributes">
			<props>
				<prop key="save*">PROPAGATION_REQUIRED</prop>
				<prop key="del*">PROPAGATION_REQUIRED</prop>
				<prop key="update*">PROPAGATION_REQUIRED</prop>
				<prop key="create*">PROPAGATION_REQUIRED</prop>
				<prop key="add*">PROPAGATION_REQUIRED</prop>
				<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
				<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
				<prop key="*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>

以上配置都OK.


我遇到的問題有幾個, 如下:


問題1,需要實現這樣一個業務邏輯: 先把對象find出來.然後改變某個屬性.然後在update. 在service當中就會寫這樣一個方法.changeOrder.在changeOrder當中先用dao的find.然後在用dao的update. 理論上是可行的.因爲service的事務都是被spring的事務模板託管.而且changeOrder得到的connection是可寫的.(因爲 spring的事務模板根據對方法名的匹配來判斷獲得得connection類型).但是由於baseDao.當中的所有find方法都是 setReadyOnly(true). 這樣.當在service執行任何find的時候.baseDao將強行把connection改爲只讀的.接下來在一個事務當中.任何update 和save動作都不能完成了.但是直接執行hibernateTemplate的get和load卻不會出現這個問題.因爲這個connection的屬 性是由spring的openSessionInView來處理的.在request一過來的時候spring會綁定一個session.到 request.直至request結束.(在這段過程當中如果不認爲改變connection的readOnly的屬性.這個connection將會 從請求一開始到結束都是可以寫入的.)



解決的辦法就是在自己的dao當中將find方法重載.將readOnly改爲false.



問題2, 有兩個方法.一個是get對象.一個是find對象.同樣也是直接調用baseDao的get和find方法.


當我對一個對象進行編輯操作的時候發現service當中的update是有效的. 但是我find出來的對象.在利用service當中的update來更新卻發現沒有任何異常.但是就是更新不了對象.


後來才明白.get方法當中是沒有對connection進行任何readOnly相關的操作.但是baseDao當中卻設置了只讀..這個時候 又有一些疑問了. action並沒有進行事務管理.當先調用service的find方法(也就是調用了baseDao中的find方法).這一個事務已經提交了.然後在 繼續調用service的update.爲什麼會更新不了對象?


原因就是在於OpenSessionInView.綁定的一個session對象在這一次的request當中.所以.從一次request.開 始到結束.這個request僅僅會操作當前的一個session對象. 儘管在action當中連續調用的兩次service方法都有兩個不同的事務範圍.在一整個請求當中還是隻存在一個session對象.


所以第一個service的find方法執行完畢之後已經將當前request範圍內的session改成了readOnly.以後的所有的 service操作都是隻讀的.後面的service一些save或者update方法都會失效.....這就是OpenSessionInView和事 務之間的微妙關係.



問題3,在ajax異步調用當中.經常也會出現這一系列的問題.其實原理大都是一樣.因爲ajax後來也是一個以.dwr結尾的請求.在 OpenSessionInView當中加入一個filtermapping 爲 .dwr 這樣它會攔截所有的.dwr請求.在所有的ajax操作當中會綁定一個session對象.



總結一下: OpenSessionInView是一個filter.它會爲每一個request綁定一個session.任何接下來在這一次請求當中所有的 hibernate操作.都是基於當前請求的這個session的.任何service或者dao把當前的session對象改爲了readOnly後. 接下來所有save or update操作將進行不了.儘管他們不是在一個service方法(不是在同一個事務當中進行).




BTW.service在很大程度上是可以和dao層混合到一起.這樣可以節約很多代碼.但是.也會帶來維護的時候非常的負責.並且麻煩.特別是readOnly 和session was closed問題.會另你非常的沮喪.


dao實際上是不需要用transaction來管理的.真正需要事務的是項目當中的service層.理解纔是最重要的.用好OpenSessionInView會給項目帶來極大的方便.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章