Spring聲明式事務配置管理方法 – JAVA編程語言程序開發技術文章

事務配置
首先在/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標簽就是把上面我們所配置的事務管理兩部分屬性整合起來作為整個事務管理。
圖解:

 
下面附上配置聲明式事務的一些相關的資料,以下資料均來源於互聯網:
附一、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>
這個方法可以針對具體的模塊進行攔截並進行事務處理。
在你的實際項目中,你可以根據你的情況選用不同的方法

發佈留言