Spring聲明式事務配置管理方法

轉自:http://blog.csdn.net/wwww1988600/article/details/7236841

事務配置

  • 首先在/WEB-INF/applicationContext.xml添加以下內容:
<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">

<property name="sessionFactory">

<ref bean="mySessionFactory"/>

</property>

</bean>

注:這是作爲公共使用的事務管理器Bean。這個會是事先配置好的,不需各個模塊各自去配。

  • 下面就開始配置各個模塊所必須的部分,在各自的applicationContext-XXX-beans.xml配置的對於事務管理的詳細信息。

首先就是配置事務的傳播特性,如下:

<!-- 配置事務傳播特性 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="REQUIRED"/>
<tx:method name="apply*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>


<!-- 配置參與事務的類 -->
<aop:config>
<aop:pointcut id="allTestServiceMethod" expression="execution(* com.test.testAda.test.model.service.*.*(..))"/>
<aop:advisor pointcut-ref="allTestServiceMethod" advice-ref="TestAdvice" />
</aop:config>

需要注意的地方:

(1) advice(建議)的命名:由於每個模塊都會有自己的Advice,所以在命名上需要作出規範,初步的構想就是模塊名+Advice(只是一種命名規範)。

(2) tx:attribute標籤所配置的是作爲事務的方法的命名類型

         如<tx:method name="save*" propagation="REQUIRED"/>

        其中*爲通配符,即代表以save爲開頭的所有方法,即表示符合此命名規則的方法作爲一個事務。

        propagation="REQUIRED"代表支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。

(3) aop:pointcut標籤配置參與事務的類,由於是在Service中進行數據庫業務操作,配的應該是包含那些作爲事務的方法的Service類。

       首先應該特別注意的是id的命名,同樣由於每個模塊都有自己事務切面,所以我覺得初步的命名規則因爲 all+模塊名+ServiceMethod。而且每個模塊之間不同之處還在於以下一句:

       expression="execution(* com.test.testAda.test.model.service.*.*(..))"

       其中第一個*代表返回值,第二*代表service下子包,第三個*代表方法名,“(..)”代表方法參數。

(4) aop:advisor標籤就是把上面我們所配置的事務管理兩部分屬性整合起來作爲整個事務管理。

圖解:

image

 

下面附上配置聲明式事務的一些相關的資料,以下資料均來源於互聯網:

附一、Spring事務類型詳解

附二、對spring事務類型詳解的一點補充(關於嵌套事務)

附三、Transaction後綴給聲明式事務管理帶來的好處

附四、Spring中的四種聲明式事務的配置

 

 

附一、Spring事務類型詳解

<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop><prop key="store*">PROPAGATION_REQUIRED</prop>

估計有好多朋友還沒有弄清楚裏面的值的意思,仔細看完下面應該知道自己什麼情況下面應該使用什麼樣的聲明。^_^

Spring中常用事務類型:

PROPAGATION_REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。

PROPAGATION_SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。

PROPAGATION_MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。

PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。

PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。

PROPAGATION_NESTED--如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與PROPAGATION_REQUIRED類似的操作。

 

附二、對spring事務類型詳解的一點補充(關於嵌套事務)

· PROPAGATION_REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。

· PROPAGATION_SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。

· PROPAGATION_MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。

· PROPAGATION_REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。

· PROPAGATION_NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。

· PROPAGATION_NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。


       可能大家對PROPAGATION_NESTED還不怎麼了解,覺得有必要再補充一下^_^!
PROPAGATION_NESTED: 嵌套事務類型,是相對上面提到的六種情況(上面的六種應該稱爲平面事務類型),打個比方我現在有一個事務主要有一下幾部分:
      1,從A用戶帳戶裏面減去100元錢
      2,往B用戶帳戶裏面添加100元錢
       這樣看和以前不同的事務可能沒有什麼區別,那我現在有點特殊的要求就是,A用戶有3個帳戶,B用戶有2個帳戶,現在我的要求就是隻要再A用戶的3個帳戶裏面任意一個減去100元,往B用戶的兩個帳戶中任意一個裏面增加100元就可以了!
       一旦你有這樣的要求那嵌套事務類型就非常適合你!我們可以這樣理解,
       一:將“從A用戶帳戶裏面減去100元錢” 和 “往B用戶帳戶裏面增加100元錢”我們暫時認爲是一級事務操作
       二:將從A用戶的3個帳戶的任意一個帳戶裏面減錢看做是“從A用戶帳戶裏面減去100元錢”這個一級事務的子事務(二級事務),同樣把後面存錢的看成是另一個的二級事務。
      問題一:當二級事務被rollback一級事務會不會被rollback?
      答案是不會的,二級事務的rollback只針對自己。
      問題二:什麼時候這個一級事務會commit,什麼時候會被rollback呢?
      我們主要看二級裏面出現的情況,當所有的二級事務被commit了並且一級事務沒有失敗的操作,那整個事務就算是一個成功的事務,這種情況整個事務會被commit。
當任意一個二級事務沒有被commit那整個事務就是失敗的,整個事務會被roolback。
還是拿上面的例子來說明吧!如果我在a的三個帳戶裏面減錢的操作都被二級事務給rollback了,也就是3個帳戶裏面都沒有減錢成功,整個事務就失敗了就會被rollback。如果A用戶帳戶三個帳戶裏面有一個可以扣錢而且B用戶的兩個帳戶裏面也有一個帳戶可以增加錢,那整個事務就算成功的,會被 commit。
看了一下覺得上面的例子好像不是很深刻,看這個情況(A用戶的3個帳戶都是有信用額度的,也就是說可以超支,但是超支有金額限制)。不過原理是一樣的,簡單點也好說明一點,祝你好運!^_^

 

附三、Transaction後綴給聲明式事務管理帶來的好處

        良好的面向對象的程序,一般都使用接口和實現分離的模式。我在《事務管理最佳實踐全面解析》一文中提出,用*Transaction和*Dao後綴這樣的形式,區分方法的不同用途。

這樣,可以提醒接口的實現者和方法的使用者注意到它們對於數據庫連接和事務的依賴。

實際上,使用*Transaction後綴這樣的命名方式,對於聲明式事務管理也是很有用處的。如,Spring的事務管理中,我們一般使用方法名的匹配來應用聲明式事務。

一、請看下面的Spring配置:

<bean id="txProxyTemplate" abstract="true" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<property name="transactionManager" ref="transactionManager"/>

<property name="transactionAttributes">

<props>

<prop key="*">readOnly</prop>

<prop key="add*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="modify*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="update*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="delete*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="remove*">PROPAGATION_REQUIRED,-Exception</prop>

<prop key="query*">PROPAGATION_REQUIRED, readOnly,-Exception</prop>

<prop key="load*">PROPAGATION_REQUIRED, -Exception</prop>

</props>

</property>

</bean>

這是來自於真實項目中的Spring聲明式事務配置。我們對每一個業務層的實現類都應用了這樣的事務配置。

我們對所有業務服務Service方法使用了只讀事務。對以add,save,modify,update,delete,remove,load開頭的方法都使用了事務。

但是,實際上,雖然我們開發的軟件一個“信息管理系統”,是圍繞數據庫開發的。但是,在Service層,我們還是有很多不操作數據庫的方法。

如,單純根據業務邏輯進行計算的,適用緩存進行計算的,執行email發送,文件上傳等等任務的方法,在這種配置下都不分青紅皁白的應用了事務。

SpringAOP生成的代理對象代理了我們的服務實現類,所有的方法執行前後都被攔截,用來得到和關閉數據庫連接,設置、提交和回滾事務。而不管這個方法是否用到了這個數據庫。

如果遵照我提出的這個方法,使用*Transaction後綴來標識需要處理事務的方法,那麼我們使用Spring聲明式事務時,就可以非常精確、有效的應用事務了!

二、請看下面的Spring事務配置:

<!-- UninstallWcmsJbpmProcessDefinition -->

<bean id="uninstallWcmsJbpmProcessDefinition" parent="txProxyTemplate">

<property name="target">

<ref bean="uninstallWcmsJbpmProcessDefinitionTarget"/>

</property>

<property name="transactionAttributes">

<props>

<prop key="uninstall*Wcms*Transaction">PROPAGATION_REQUIRED,-Exception</prop>

</props>

</property>

</bean>

我們對這個類中以uninstall開頭,中間包含Wcms,最後以Transaction結尾,這樣的規則命名的方法,應用了事務。

三、部分源代碼:

(一)2個應用了Spring聲明式事務的方法:

/** 
*使用SPring的ibatis,主要要配置iBatis的Spring聲明式事務。 
*@throwsException 
*<prop key="uninstall*Wcms*Transaction">PROPAGATION_REQUIRED,-Exception</prop> 
*1,還要刪除所有 頻道---新聞--工作流表中標記不爲1的記錄。 
*/ 
publicvoid uninstallAllWcmsProcessDefinitionsTransaction() throws Exception{ 
/** 
*/ 
this.getWcmsSystemChannelProcessdefinitionDao().deleteAll(); 
this.getWcmsSystemChannelNewsinfoDao().deleteAllProcessingWcmsSystemChannelNewsinfoModule(); 
    
/** 
*<prop key="uninstall*Wcms*Transaction">PROPAGATION_REQUIRED,-Exception</prop> 
*@paramname 
*@throwsException 
*/ 
publicvoid uninstallWcmsSystemChannelProcessdefinitionTransaction(String name) throws Exception{ 
this.getWcmsSystemChannelProcessdefinitionDao().deleteByProcessdefinitionName(name); 
this.getWcmsSystemChannelNewsinfoDao().deleteAllProcessingWcmsSystemChannelNewsinfoModuleByProcessdefinitionName(name); 
    
(二)用到的Dao類,用來實際訪問數據庫的2個DAO對象。
  
/**
  
*SPring管理的ibatis功能
  
*/
  
private IWcmsSystemChannelProcessdefinitionDao wcmsSystemChannelProcessdefinitionDao;
  
private IWcmsSystemChannelNewsinfoDao wcmsSystemChannelNewsinfoDao;

 

附四、Spring中的四種聲明式事務的配置

讓我們言歸正傳吧。

以下兩個bean的配置是下面要用到的。

<!-- 定義事務管理器(聲明式的事務) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref local="sessionFactory" />
</property>
</bean>

<!-- *******業務邏輯層(是對各個DAO層的正面封裝)主要用到<<門面模式>>****** -->
<bean id="fundService"
class="com.jack.fund.service.serviceimpl.FundService">
<property name="operdao">
<ref bean="operatorDAO" />
</property>
<property name="producedao">
<ref bean="fundProduceDAO" />
</property>
<property name="customerdao">
<ref bean="customerDAO" />
</property>
<property name="accountdao">
<ref bean="accountDAO" />
</property>
<property name="fundaccountdao">
<ref bean="fundAccountDAO" />
</property>
<property name="fundtransdao">
<ref bean="fundTransDAO" />
</property>
</bean>

可能還有其他很多模塊。<bean id="fundService"/>可能只是其中的模塊。

第一種:配置聲明式事務的方法如下。也是我們最常用的方法了,它適用於你的庫表比較少的情況下。

<bean id="fundServiceDAOProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置事務管理器 -->
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<!-- 此屬性指定目標類本省是否是代理的對象,如果目標類沒有實現任何類,就設爲true代表自己 -->
<property name="proxyTargetClass">
<value>false</value>
</property>
<property name="proxyInterfaces">
<value>com.jack.fund.service.IFundService</value>
</property>
<!-- 目標bean -->
<property name="target">
<ref bean="fundService" />
</property>
<!-- 配置事務屬性 -->
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

以下可能還有其他的xxxServiceDAOProxy.大家可以看出針對每一個功能模塊配置一個業務代理服務。如果模塊多大話,就顯得代碼有點多了,發現他們只是稍微一點不一樣。這時我們就應該想到繼承的思想。用第二種方法。

第二種:配置聲明式事務的方法如下。這種情況適合相對比較多的模塊時使用。

<!-- 利用繼承的思想簡化配置,要把abstract="true" -->
<bean id="transactionBase"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true" abstract="true">
<!-- 配置事務管理器 -->
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<!-- 配置事務屬性 -->
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

而具體的模塊可以簡單的這樣配置。只要指明它的parent(父類)就可以了。父類一般把abstract="true",因爲在容器加載的時候不需要初始化,等到用的時候再有它的子類調用的時候,再去初始化。

<bean id="fundServiceDAOProxy" parent="transactionBase" >
<property name="target">
<ref bean="fundService" />
</property>
</bean>

這樣配置的話,如果有多個像fundService這樣模塊時,可以少些很多重複的代碼。

第三種:配置聲明式事務的方法如下。主要利用BeanNameAutoProxyCreator自動創建事務代理

<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">

<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<!-- 配置事務屬性 -->
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

<bean
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>fundService</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>

這種方法主要利用了攔截器的原理。

前三種方法一般都必需指定具體的模塊bean.如果模塊過多話,比如一個大型的網站一般有幾十個模塊。我們就得考慮用第四種的配置方式了。自動創建事務代理的方式了。

第四種:配置聲明式事務的方法如下。

<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">

<property name="transactionManager">
<ref bean="transactionManager" />
</property>

<!-- 自動代理 -->
<bean id="autoproxy"
class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<!-- 可以是Service或DAO層(最好是針對業務層*Service) -->
<property name="beanNames">
<list>
<value>*Service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>

自動代理還有一種用法就是結合正規表達式和advice使用。

<bean id="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">

<property name="transactionManager">
<ref bean="transactionManager" />
</property>

<bean id="autoProxyCreator"
class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

<bean id="regexpMethodPointcutAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref bean="transactionInterceptor" />
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean>

這個方法可以針對具體的模塊進行攔截並進行事務處理。

在你的實際項目中,你可以根據你的情況選用不同的方法。


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