[原]Spring事務配置的五種方式 巨全!不看後悔,一看必懂!

前段時間對Spring的事務配置作了比較深刻的研究,在此之間對Spring的事務配置雖然說也配置過,可是一直沒有一個清楚的認識。經過此次的學習發覺Spring的事務配置只要把思路理清,仍是比較好掌握的。html

    總結以下:java

    Spring配置文件中關於事務配置老是由三個組成部分,分別是DataSourceTransactionManager和代理機制這三部分,不管哪一種配置方式,通常變化的只是代理機制這部分。程序員

    DataSourceTransactionManager這兩部分只是會根據數據訪問方式有所變化,好比使用Hibernate進行數據訪問時,DataSource實際爲SessionFactoryTransactionManager的實現爲HibernateTransactionManagerspring

    具體以下圖: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聲明式事務時,有一個很是重要的概念就是事務屬性。事務屬性一般由事務的傳播行爲,事務的隔離級別,事務的超時值和事務只讀標誌組成。咱們在進行事務劃分時,須要進行事務定義,也就是配置事務的屬性。 
SpringTransactionDefinition接口中定義這些屬性,以供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,但未提交事務 

Java代碼  

1. Connection con1 = getConnection();  

2. con.setAutoCommit(false);  

3. update employee set salary = 8000 where empId ="Mary";  


與此同時,Mary正在讀取本身的工資 

Java代碼  

1. Connection con2 = getConnection();  

2. select  salary from employee where empId ="Mary";  

3. con2.commit();  



Mary發現本身的工資變爲了8000,歡天喜地! 
而財務發現操做有誤,而回滾了事務,Mary的工資又變爲了1000 

Java代碼  

1. //con1  

2.   con1.rollback();  


像這樣,Mary記取的工資數8000是一個髒數據。 

ISOLATION_READ_COMMITTED  保證一個事務修改的數據提交後才能被另一個事務讀取。另一個事務不能讀取該事務未提交的數據。這種事務隔離級別能夠避免髒讀出現,可是可能會出現不可重複讀和幻像讀。 

ISOLATION_REPEATABLE_READ  這種事務隔離級別能夠防止髒讀,不可重複讀。可是可能出現幻像讀。它除了保證一個事務不能讀取另外一個事務未提交的數據外,還保證了避免下面的狀況產生(不可重複讀)。 

在事務1中,Mary 讀取了本身的工資爲1000,操做並無完成 

Java代碼  

1. con1 = getConnection();  

2. select salary from employee empId ="Mary";  



在事務2中,這時財務人員修改了Mary的工資爲2000,並提交了事務. 

Java代碼  

1. con2 = getConnection();  

2. update employee set salary = 2000;  

3. con2.commit();  



在事務1中,Mary 再次讀取本身的工資時,工資變爲了2000 

Java代碼  

1. //con1  

2. select salary from employee empId ="Mary";  



在一個事務中先後兩次讀取的結果並不致,致使了不可重複讀。 
使用ISOLATION_REPEATABLE_READ能夠避免這種狀況發生。 

ISOLATION_SERIALIZABLE 這是花費最高代價可是最可靠的事務隔離級別。事務被處理爲順序執行。除了防止髒讀,不可重複讀外,還避免了幻像讀。 

目前工資爲1000的員工有10人。 
事務1,讀取全部工資爲1000的員工。 

Java代碼  

1. con1 = getConnection();  

2. Select * from employee where salary =1000;  

共讀取10條記錄 

這時另外一個事務向employee表插入了一條員工記錄,工資也爲1000 

Java代碼  

1. con2 = getConnection();  

2. Insert into employee(empId,salary) values("Lili",1000);  

3. con2.commit();  



事務1再次讀取全部工資爲1000的員工 

Java代碼  

1. //con1  

2. select * from employee where salary =1000;  



共讀取到了11條記錄,這就產生了幻像讀。 
ISOLATION_SERIALIZABLE能避免這樣的狀況發生。可是這樣也耗費了最大的資源。 

getPropagationBehavior()返回事務的傳播行爲,由是否有一個活動的事務來決定一個事務調用。 

在TransactionDefinition接口中定義了七個事務傳播行爲。 

PROPAGATION_REQUIRED 若是存在一個事務,則支持當前事務。若是沒有事務則開啓一個新的事務。 

Java代碼  

1. //事務屬性 PROPAGATION_REQUIRED  

2. methodA{  

3. ……  

4. methodB();  

5. ……  

6. }  

7.   

8. //事務屬性 PROPAGATION_REQUIRED  

9. methodB{  

10.   ……  

11.}  


使用spring聲明式事務,spring使用AOP來支持聲明式事務,會根據事務屬性,自動在方法調用以前決定是否開啓一個事務,並在方法執行以後決定事務提交或回滾事務。 

單獨調用methodB方法 

Java代碼  

1. main{  

2.   metodB();  

3. }  


至關於 

Java代碼  

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. 

執行效果至關於 

Java代碼  

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與不使用事務有少量不一樣。 

Java代碼  

1. //事務屬性 PROPAGATION_REQUIRED   

2. methodA(){  

3.   methodB();  

4. }  

5.   

6. //事務屬性 PROPAGATION_SUPPORTS   

7. methodB(){  

8.   ……  

9. }  


單純的調用methodB時,methodB方法是非事務的執行的。 
當調用methdA時,methodB則加入了methodA的事務中,事務地執行。 

PROPAGATION_MANDATORY 若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常。 

Java代碼  

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 老是開啓一個新的事務。若是一個事務已經存在,則將這個存在的事務掛起。 

Java代碼  

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時 

Java代碼  

1. main(){  

2.   methodA();  

3. }  

狀況有些大不同.至關於下面的效果。 

Java代碼  

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  老是非事務地執行,並掛起任何存在的事務。 

Java代碼  

1. //事務屬性 PROPAGATION_REQUIRED   

2. methodA(){  

3.   doSomeThingA();  

4. methodB();  

5. doSomeThingB();  

6. }  

7.   

8. //事務屬性 PROPAGATION_NOT_SUPPORTED   

9. methodB(){  

10.  ……  

11.}  


當單獨調用methodB時,不啓用任何事務機制,非事務地執行。 
當調用methodA時,至關於下面的效果 

Java代碼  

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 老是非事務地執行,若是存在一個活動事務,則拋出異常 

Java代碼  

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; 

Java代碼  

1. //事務屬性 PROPAGATION_REQUIRED   

2. methodA(){  

3.   doSomeThingA();  

4. methodB();  

5. doSomeThingB();  

6. }  

7.   

8. //事務屬性 PROPAGATION_NESTED  

9. methodB(){  

10.  ……  

11.}  


若是單獨調用methodB方法,則按REQUIRED屬性執行。 

若是調用methodA方法,至關於下面的效果 

Java代碼  

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");          }   

Java代碼  

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註解的noRollbackForrollbackFor屬性

如:@Transactional(rollbackFor=Exception.class)可使checked異常發生時,數據庫操做也rollback@Transactional(noRollbackFor=RuntimeException.class)可使unchecked異常發生時也提交數據庫操做。

也可使用noRollbackForClassNamerollbackForClassName屬性來指定一個異常類名的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有效。

 

事務的隔離級別

 
使用@TransactionalIsolation屬性能夠指定事務的隔離級別。但事務的隔離級別是由底層的數據庫實現的,並非由Spring來實現。

1.       READ_UNCOMMITTED
:會出現髒讀、不可重複讀和幻讀問題;

2.       READ_COMMITTED
:會出現不可重複讀和幻讀問題;

3.       REPEATABLE_READ
:會出現幻讀問題;

4.       SERIALIZABLE
:串行化,不會出現上面的問題。

 
通常的數據庫默認提供的是READ_COMMITTED隔離級別,如sqlserver2000Mysql默認提供的是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("
註釋");
    }
 


相關文章
相關標籤/搜索