前段時間對Spring的事務配置作了比較深刻的研究,在此之間對Spring的事務配置雖然說也配置過,可是一直沒有一個清楚的認識。經過此次的學習發覺Spring的事務配置只要把思路理清,仍是比較好掌握的。html
總結以下:java
Spring配置文件中關於事務配置老是由三個組成部分,分別是DataSource、TransactionManager和代理機制這三部分,不管哪一種配置方式,通常變化的只是代理機制這部分。程序員
DataSource、TransactionManager這兩部分只是會根據數據訪問方式有所變化,好比使用Hibernate進行數據訪問時,DataSource實際爲SessionFactory,TransactionManager的實現爲HibernateTransactionManager。spring
具體以下圖:sql
根據代理機制的不一樣,總結了五種Spring事務的配置方式,配置文件以下:數據庫
第一種方式:每一個Bean都有一個代理express
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 配置事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <property name="target" ref="userDaoTarget" /> <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> </beans>
第二種方式:全部Bean共享一個代理基類數組
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" lazy-init="true" abstract="true"> <!-- 配置事務管理器 --> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!-- 配置DAO --> <bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="userDao" parent="transactionBase" > <property name="target" ref="userDaoTarget" /> </bean> </beans>
第三種方式:使用攔截器session
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <property name="transactionManager" ref="transactionManager" /> <!-- 配置事務屬性 --> <property name="transactionAttributes"> <props> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames"> <list> <value>*Dao</value> </list> </property> <property name="interceptorNames"> <list> <value>transactionInterceptor</value> </list> </property> </bean> <!-- 配置DAO --> <bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
第四種方式:使用tx標籤配置的攔截器dom
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="*" propagation="REQUIRED" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="interceptorPointCuts" expression="execution(* com.bluesky.spring.dao.*.*(..))" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" /> </aop:config> </beans>
第五種方式:全註解
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <context:annotation-config /> <context:component-scan base-package="com.bluesky" /> <tx:annotation-driven transaction-manager="transactionManager"/> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> <property name="configLocation" value="classpath:hibernate.cfg.xml" /> <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" /> </bean> <!-- 定義事務管理器(聲明式的事務) --> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean> </beans>
此時在DAO上需加上@Transactional註解,以下:
package com.bluesky.spring.dao; import java.util.List; import org.hibernate.SessionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.springframework.stereotype.Component; import com.bluesky.spring.domain.User; @Transactional @Component("userDao") public class UserDaoImpl extends HibernateDaoSupport implements UserDao { public List<User> listUsers() { return this.getSession().createQuery("from User").list(); } }
Spring聲明式事務讓咱們從複雜的事務處理中獲得解脫。使得咱們再也無須要去處理得到鏈接、關閉鏈接、事務提交和回滾等這些操做。再也無須要咱們在與事務相關的方法中處理大量的try…catch…finally代碼。
咱們在使用Spring聲明式事務時,有一個很是重要的概念就是事務屬性。事務屬性一般由事務的傳播行爲,事務的隔離級別,事務的超時值和事務只讀標誌組成。咱們在進行事務劃分時,須要進行事務定義,也就是配置事務的屬性。
Spring在TransactionDefinition接口中定義這些屬性,以供PlatfromTransactionManager使用, PlatfromTransactionManager是spring事務管理的核心接口。
1. TransactionDefinition 2. public interface TransactionDefinition { 3. int getPropagationBehavior(); 4. int getIsolationLevel(); 5. int getTimeout(); 6. boolean isReadOnly(); 7. }
getTimeout()方法,它返回事務必須在多少秒內完成。
isReadOnly(),事務是否只讀,事務管理器可以根據這個返回值進行優化,確保事務是隻讀的。
getIsolationLevel()方法返回事務的隔離級別,事務管理器根據它來控制另一個事務能夠看到本事務內的哪些數據。
在TransactionDefinition接口中定義了五個不一樣的事務隔離級別
ISOLATION_DEFAULT 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.另外四個與JDBC的隔離級別相對應
ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級別,它充許別外一個事務能夠看到這個事務未提交的數據。這種隔離級別會產生髒讀,不可重複讀和幻像讀。
例如:
Mary的原工資爲1000,財務人員將Mary的工資改成了8000,但未提交事務
1. Connection con1 = getConnection();
2. con.setAutoCommit(false);
3. update employee set salary = 8000 where empId ="Mary";
與此同時,Mary正在讀取本身的工資
1. Connection con2 = getConnection();
2. select salary from employee where empId ="Mary";
3. con2.commit();
Mary發現本身的工資變爲了8000,歡天喜地!
而財務發現操做有誤,而回滾了事務,Mary的工資又變爲了1000
1. //con1
2. con1.rollback();
像這樣,Mary記取的工資數8000是一個髒數據。
ISOLATION_READ_COMMITTED 保證一個事務修改的數據提交後才能被另一個事務讀取。另一個事務不能讀取該事務未提交的數據。這種事務隔離級別能夠避免髒讀出現,可是可能會出現不可重複讀和幻像讀。
ISOLATION_REPEATABLE_READ 這種事務隔離級別能夠防止髒讀,不可重複讀。可是可能出現幻像讀。它除了保證一個事務不能讀取另外一個事務未提交的數據外,還保證了避免下面的狀況產生(不可重複讀)。
在事務1中,Mary 讀取了本身的工資爲1000,操做並無完成
1. con1 = getConnection();
2. select salary from employee empId ="Mary";
在事務2中,這時財務人員修改了Mary的工資爲2000,並提交了事務.
1. con2 = getConnection();
2. update employee set salary = 2000;
3. con2.commit();
在事務1中,Mary 再次讀取本身的工資時,工資變爲了2000
1. //con1
2. select salary from employee empId ="Mary";
在一個事務中先後兩次讀取的結果並不致,致使了不可重複讀。
使用ISOLATION_REPEATABLE_READ能夠避免這種狀況發生。
ISOLATION_SERIALIZABLE 這是花費最高代價可是最可靠的事務隔離級別。事務被處理爲順序執行。除了防止髒讀,不可重複讀外,還避免了幻像讀。
目前工資爲1000的員工有10人。
事務1,讀取全部工資爲1000的員工。
1. con1 = getConnection();
2. Select * from employee where salary =1000;
共讀取10條記錄
這時另外一個事務向employee表插入了一條員工記錄,工資也爲1000
1. con2 = getConnection();
2. Insert into employee(empId,salary) values("Lili",1000);
3. con2.commit();
事務1再次讀取全部工資爲1000的員工
1. //con1
2. select * from employee where salary =1000;
共讀取到了11條記錄,這就產生了幻像讀。
ISOLATION_SERIALIZABLE能避免這樣的狀況發生。可是這樣也耗費了最大的資源。
getPropagationBehavior()返回事務的傳播行爲,由是否有一個活動的事務來決定一個事務調用。
在TransactionDefinition接口中定義了七個事務傳播行爲。
PROPAGATION_REQUIRED 若是存在一個事務,則支持當前事務。若是沒有事務則開啓一個新的事務。
1. //事務屬性 PROPAGATION_REQUIRED
2. methodA{
3. ……
4. methodB();
5. ……
6. }
7.
8. //事務屬性 PROPAGATION_REQUIRED
9. methodB{
10. ……
11.}
使用spring聲明式事務,spring使用AOP來支持聲明式事務,會根據事務屬性,自動在方法調用以前決定是否開啓一個事務,並在方法執行以後決定事務提交或回滾事務。
單獨調用methodB方法
1. main{
2. metodB();
3. }
至關於
1. Main{
2. Connection con=null;
3.
4. rry{
5. con = getConnection();
6. con.setAutoCommit(false);
7. //方法調用
8. methodB();
9. //提交事務
10.con.commit();
11.}
12.Catch(RuntimeException ex){
13. //回滾事務
14. con.rollback();
15.}
16.finally{
17. //釋放資源
18. closeCon();
19.}
20.}
Spring保證在methodB方法中全部的調用都得到到一個相同的鏈接。在調用methodB時,沒有一個存在的事務,因此得到一個新的鏈接,開啓了一個新的事務。
單獨調用MethodA時,在MethodA內又會調用MethodB.
執行效果至關於
1. main{
2. Connection con = null;
3. try{
4. con = getConnection();
5. methodA();
6. con.commit();
7. }
8. cathc(RuntimeException ex){
9. con.rollback();
10.}
11.finally{
12. closeCon();
13.}
14.}
調用MethodA時,環境中沒有事務,因此開啓一個新的事務.
當在MethodA中調用MethodB時,環境中已經有了一個事務,因此methodB就加入當前事務。
PROPAGATION_SUPPORTS 若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行。可是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少量不一樣。
1. //事務屬性 PROPAGATION_REQUIRED
2. methodA(){
3. methodB();
4. }
5.
6. //事務屬性 PROPAGATION_SUPPORTS
7. methodB(){
8. ……
9. }
單純的調用methodB時,methodB方法是非事務的執行的。
當調用methdA時,methodB則加入了methodA的事務中,事務地執行。
PROPAGATION_MANDATORY 若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常。
1. //事務屬性 PROPAGATION_REQUIRED
2. methodA(){
3. methodB();
4. }
5.
6. //事務屬性 PROPAGATION_MANDATORY
7. methodB(){
8. ……
9. }
當單獨調用methodB時,由於當前沒有一個活動的事務,則會拋出異常
throw new IllegalTransactionStateException("Transactionpropagation 'mandatory' but no existing transaction found");
當調用methodA時,methodB則加入到methodA的事務中,事務地執行。
PROPAGATION_REQUIRES_NEW 老是開啓一個新的事務。若是一個事務已經存在,則將這個存在的事務掛起。
1. //事務屬性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事務屬性 PROPAGATION_REQUIRES_NEW
9. methodB(){
10. ……
11.}
當單獨調用methodB時,至關於把methodb聲明爲REQUIRED。開啓一個新的事務,事務地執行。
當調用methodA時
1. main(){
2. methodA();
3. }
狀況有些大不同.至關於下面的效果。
1. main(){
2. TransactionManager tm = null;
3. try{
4. //得到一個JTA事務管理器
5. tm = getTransactionManager();
6. tm.begin();//開啓一個新的事務
7. Transaction ts1 = tm.getTransaction();
8. doSomeThing();
9. tm.suspend();//掛起當前事務
10. try{
11. tm.begin();//從新開啓第二個事務
12. Transaction ts2 = tm.getTransaction();
13. methodB();
14. ts2.commit();//提交第二個事務
15.
16. }
17. Catch(RunTimeException ex){
18. ts2.rollback();//回滾第二個事務
19. }
20. finally{
21. //釋放資源
22. }
23. //methodB執行完後,復恢第一個事務
24. tm.resume(ts1);
25.doSomeThingB();
26. ts1.commit();//提交第一個事務
27.}
28.catch(RunTimeException ex){
29. ts1.rollback();//回滾第一個事務
30.}
31.finally{
32. //釋放資源
33.}
34.}
在這裏,我把ts1稱爲外層事務,ts2稱爲內層事務。從上面的代碼能夠看出,ts2與ts1是兩個獨立的事務,互不相干。Ts2是否成功並不依賴於ts1。若是methodA方法在調用methodB方法後的doSomeThingB方法失敗了,而methodB方法所作的結果依然被提交。而除了methodB以外的其它代碼致使的結果卻被回滾了。
使用PROPAGATION_REQUIRES_NEW,須要使用JtaTransactionManager做爲事務管理器。
PROPAGATION_NOT_SUPPORTED 老是非事務地執行,並掛起任何存在的事務。
1. //事務屬性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事務屬性 PROPAGATION_NOT_SUPPORTED
9. methodB(){
10. ……
11.}
當單獨調用methodB時,不啓用任何事務機制,非事務地執行。
當調用methodA時,至關於下面的效果
1. main(){
2. TransactionManager tm = null;
3. try{
4. //得到一個JTA事務管理器
5. tm = getTransactionManager();
6. tm.begin();//開啓一個新的事務
7. Transaction ts1 = tm.getTransaction();
8. doSomeThing();
9. tm.suspend();//掛起當前事務
10. methodB();
11. //methodB執行完後,復恢第一個事務
12. tm.resume(ts1);
13.doSomeThingB();
14. ts1.commit();//提交第一個事務
15.}
16.catch(RunTimeException ex){
17. ts1.rollback();//回滾第一個事務
18.}
19.finally{
20. //釋放資源
21.}
22.}
使用PROPAGATION_NOT_SUPPORTED,也須要使用JtaTransactionManager做爲事務管理器。
PROPAGATION_NEVER 老是非事務地執行,若是存在一個活動事務,則拋出異常
1. //事務屬性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事務屬性 PROPAGATION_NEVER
9. methodB(){
10. ……
11.}
單獨調用methodB,則非事務的執行。
調用methodA則會拋出異常
throw new IllegalTransactionStateException(
"Transaction propagation 'never' butexisting transaction found");
PROPAGATION_NESTED若是一個活動的事務存在,則運行在一個嵌套的事務中. 若是沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行
這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager做爲事務管理器。須要JDBC 驅動的java.sql.Savepoint類。有一些JTA的事務管理器實現可能也提供了一樣的功能。
使用PROPAGATION_NESTED,還須要把PlatformTransactionManager的nestedTransactionAllowed屬性設爲true;
而nestedTransactionAllowed屬性值默認爲false;
1. //事務屬性 PROPAGATION_REQUIRED
2. methodA(){
3. doSomeThingA();
4. methodB();
5. doSomeThingB();
6. }
7.
8. //事務屬性 PROPAGATION_NESTED
9. methodB(){
10. ……
11.}
若是單獨調用methodB方法,則按REQUIRED屬性執行。
若是調用methodA方法,至關於下面的效果
1. main(){
2. Connection con = null;
3. Savepoint savepoint = null;
4. try{
5. con = getConnection();
6. con.setAutoCommit(false);
7. doSomeThingA();
8. savepoint = con2.setSavepoint();
9. try
10. methodB();
11. }catch(RuntimeException ex){
12. con.rollback(savepoint);
13. }
14. finally{
15. //釋放資源
16. }
17.
18. doSomeThingB();
19. con.commit();
20.}
21.catch(RuntimeException ex){
22. con.rollback();
23.}
24.finally{
25. //釋放資源
26.}
27.}
當methodB方法調用以前,調用setSavepoint方法,保存當前的狀態到savepoint。若是methodB方法調用失敗,則恢復到以前保存的狀態。可是須要注意的是,這時的事務並無進行提交,若是後續的代碼(doSomeThingB()方法)調用失敗,則回滾包括methodB方法的全部操做。
嵌套事務一個很是重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所作的動做。而內層事務操做失敗並不會引發外層事務的回滾。
PROPAGATION_NESTED與PROPAGATION_REQUIRES_NEW的區別:它們很是相似,都像一個嵌套事務,若是不存在一個活動的事務,都會開啓一個新的事務。使用PROPAGATION_REQUIRES_NEW時,內層事務與外層事務就像兩個獨立的事務同樣,一旦內層事務進行了提交後,外層事務不能對其進行回滾。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它須要JTA事務管理器的支持。
使用PROPAGATION_NESTED時,外層事務的回滾能夠引發內層事務的回滾。而內層事務的異常並不會致使外層事務的回滾,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED時,須要JDBC 3.0以上驅動及1.4以上的JDK版本支持。其它的JTA TrasactionManager實現可能有不一樣的支持方式。
PROPAGATION_REQUIRED應該是咱們首先的事務傳播行爲。它可以知足咱們大多數的事務需求。
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = { AppBizExeA.class } , noRollbackFor = { AppBizExeB.class }) public void method1() throws Exception { System.out.println("method1 start"); TPerson per = new TPerson(); per.setAge("24"); per.setId(123); per.setName("xj"); personDao.add(per); throw new NullPointerException(); } @Transactional(propagation = Propagation.NESTED, rollbackFor = { AppBizExeA.class }) public void method2() throws Exception { System.out.println("method2 start"); TPerson per = new TPerson(); per.setAge("24"); per.setId(1234); per.setName("xj"); personDao.add(per); System.out.println("method2 end"); }
1. public static void main(String[] args) throws Exception {
2.
3. ApplicationContext context = new ClassPathXmlApplicationContext(new String[] { "classpath:com/benx/benx.xml" });
4. PersonService service = (PersonService) context.getBean("personService");
5. service.method1();
6.
7. }
今天只談傳播行爲爲REQUIRED的,其餘先不談,由於使用比較少,並且支持也不夠好
一、執行service.method1(),無論method裏面是否嵌套了method2或其餘,事務都是以method1開始和結尾,且事務配置一樣以method1爲準,忽略其餘REQUIRED行爲的配置,好比異常機制,以method1爲準,忽略method2的異常配置
二、Transactional的異常控制,默認是Check Exception 不回滾,unCheck Exception回滾
三、若是配置了rollbackFor 和 noRollbackFor 且兩個都是用一樣的異常,那麼遇到該異常,仍是回滾
四、rollbackFor 和noRollbackFor 配置也許不會含蓋全部異常,對於遺漏的按照Check Exception 不回滾,unCheck Exception回滾
@Transactional只能被應用到public方法上, 對於其它非public的方法,若是標記了@Transactional也不會報錯,但方法沒有事務功能.
Spring使用聲明式事務處理,默認狀況下,若是被註解的數據庫操做方法中發生了unchecked異常,全部的數據庫操做將rollback;若是發生的異常是checked異常,默認狀況下數據庫操做仍是會提交的。
這種默認的行爲是能夠改變的。
使用@Transactional註解的noRollbackFor和rollbackFor屬性
如:@Transactional(rollbackFor=Exception.class)可使checked異常發生時,數據庫操做也rollback、@Transactional(noRollbackFor=RuntimeException.class)可使unchecked異常發生時也提交數據庫操做。
也可使用noRollbackForClassName、rollbackForClassName屬性來指定一個異常類名的String數組來改變默認的行爲。
讀取數據庫中的數據時是不須要事務管理的,這種狀況下可使用事務的傳播行爲來告訴Spring不須要開啓事務,
如:@Transactional(propagation =Propagation.NOT_SUPPORTED)。
事務的傳播行爲有:
1. REQUIRED:表示業務方法須要在一個事務中處理,若是業務方法執行時已經在一個事務中,則加入該事務,不然從新開啓一個事務。這也是默認的事務傳播行爲;
2. NOT_SUPPORTED:聲明業務方法不須要事務,若是業務方法執行時已經在一個事務中,則事務被掛起,等方法執行完畢後,事務恢復進行;
3. REQUIRES_NEW:代表業務方法須要在一個單獨的事務中進行,若是業務方法進行時已經在一個事務中,則這個事務被掛起,並從新開啓一個事務來執行這個業務方法,業務方法執行完畢後,原來的事務恢復進行;
4. MANDATORY:該屬性指定業務方法只能在一個已經存在的事務中進行,業務方法不能發起本身的事務;若是業務方法沒有在一個既有的事務中進行,容器將拋出異常;
5. SUPPORTS:該屬性指定,若是業務方法在一個既有的事務中進行,則加入該事務;不然,業務方法將在一個沒有事務的環境下進行;
6. NEVER:指定業務方法不能夠在事務中進行,若是業務方法執行時已經在一個事務中,容器將拋出異常;
7. NESTED:該屬性指定,若是業務方法在一個既有的事務中執行,則該業務方法將在一個嵌套的事務中進行;不然,按照REQUEIRED來對待。它使用一個單獨的事務,這個事務能夠有多個rollback點,內部事務的rollback對外部事務沒有影響,但外部事務的rollback會致使內部事務的rollback。這個行爲只對DataSourceTransactionManager有效。
事務的隔離級別
使用@Transactional的Isolation屬性能夠指定事務的隔離級別。但事務的隔離級別是由底層的數據庫實現的,並非由Spring來實現。
1. READ_UNCOMMITTED:會出現髒讀、不可重複讀和幻讀問題;
2. READ_COMMITTED:會出現不可重複讀和幻讀問題;
3. REPEATABLE_READ:會出現幻讀問題;
4. SERIALIZABLE:串行化,不會出現上面的問題。
通常的數據庫默認提供的是READ_COMMITTED隔離級別,如sqlserver2000;Mysql默認提供的是REPEATABLE_READ。
@Transactional 的全部可選屬性以下:
屬性 類型 默認值 說明
propagation Propagation枚舉 REQUIRED 事務傳播屬性
isolation isolation枚舉 DEFAULT 事務隔離級別
readOnly boolean false 是否只讀
timeout int -1 超時(秒)
rollbackFor Class[] {} 須要回滾的異常類
rollbackForClassName String[] {} 須要回滾的異常類名
noRollbackFor Class[] {} 不須要回滾的異常類
noRollbackForClassName String[] {} 不須要回滾的異常類名
//事務傳播屬性
@Transactional(propagation=Propagation.REQUIRED) //若是有事務,那麼加入事務,沒有的話新建一個(不寫的狀況下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) //容器不爲這個方法開啓事務
@Transactional(propagation=Propagation.REQUIRES_NEW) //不論是否存在事務,都建立一個新的事務,原來的掛起,新的執行完畢,繼續執行老的事務
@Transactional(propagation=Propagation.MANDATORY) //必須在一個已有的事務中執行,不然拋出異常
@Transactional(propagation=Propagation.NEVER) //必須在一個沒有的事務中執行,不然拋出異常(與Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) //若是其餘bean調用這個方法,在其餘bean中聲明事務,那就用事務.若是其餘bean沒有聲明事務,那就不用事務.
@Transactional(propagation=Propagation.NESTED)
@Transactional (propagation =Propagation.REQUIRED,readOnly=true) //readOnly=true只讀,不能更新,刪除
@Transactional (propagation =Propagation.REQUIRED,timeout=30)//設置超時時間
@Transactional (propagation =Propagation.REQUIRED,isolation=Isolation.DEFAULT)//設置數據庫隔離級別
用 spring 事務管理器,由spring來負責數據庫的打開,提交,回滾.
默認遇到運行期例外(throw new RuntimeException("註釋");)會回滾,即遇到不受檢查(unchecked)的例外時回滾;
而遇到須要捕獲的例外(throw new Exception("註釋");)不會回滾,即遇到受檢查的例外(就是非運行時拋出的異常,編譯器會檢查到的異常叫受檢查例外或說受檢查異常)時,需咱們指定方式來讓事務回滾 ,以下:
@Transactional(rollbackFor=Exception.class) //指定回滾,遇到異常Exception時回滾
public void methodName() {
throw new Exception("註釋");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滾,遇到運行期例外(throw newRuntimeException("註釋");)會回滾
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("註釋");
}