項目使用SSH架構,如今要添加Spring事務管理功能,針對當前環境,只須要添加Spring 2.0 AOP類庫便可。添加方法:java
點擊項目右鍵->Build Path->Add librarys:web
打開Add Libraries對話框,而後選定 MyEclipse Libraries:spring
點擊Next,找到Spring 2.0 aop Libraries並勾選上,點擊finsh便可。數據庫
若是在項目裏面能看到下面的庫文件,說明已經安裝成功。express
首先在/WEB-INF/applicationContext.xml添加如下內容:緩存
<!-- 配置事務管理器 --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory"> <ref bean="mySessionFactory"/> </property> </bean>
注:這是做爲公共使用的事務管理器Bean。這個會是事先配置好的,不需各個模塊各自去配。session
下面就開始配置各個模塊所必須的部分,在各自的applicationContext-XXX-beans.xml配置的對於事務管理的詳細信息。架構
首先就是配置事務的傳播特性,以下:app
<!-- 配置事務傳播特性 --> <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標籤就是把上面咱們所配置的事務管理兩部分屬性整合起來做爲整個事務管理。
圖解:
下面附上配置聲明式事務的一些相關的資料,如下資料均來源於互聯網:
附1、Spring事務類型詳解
附2、對spring事務類型詳解的一點補充(關於嵌套事務)
附3、Transaction後綴給聲明式事務管理帶來的好處
附4、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相似的操做。
· 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和*Dao後綴這樣的形式,區分方法的不一樣用途。
這樣,能夠提醒接口的實現者和方法的使用者注意到它們對於數據庫鏈接和事務的依賴。
實際上,使用*Transaction後綴這樣的命名方式,對於聲明式事務管理也是頗有用處的。如,Spring的事務管理中,咱們通常使用方法名的匹配來應用聲明式事務。
1、請看下面的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聲明式事務時,就能夠很是精確、有效的應用事務了!
2、請看下面的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結尾,這樣的規則命名的方法,應用了事務。
3、部分源代碼:
(一)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; |
讓咱們言歸正傳吧。
如下兩個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>
這個方法能夠針對具體的模塊進行攔截並進行事務處理。
在你的實際項目中,你能夠根據你的狀況選用不一樣的方法。