Spring教程第四篇:Spring事務

一、事務的作用

  將若干的數據庫操作作爲一個整體控制,一起成功或一起失敗。
  原子性:指事務是一個不可分割的工作單位,事務中的操作要麼都發生,要麼都不發生。
  一致性:指事務前後數據的完整性必須保持一致。
  隔離性:指多個用戶併發訪問數據庫時,一個用戶的事務不能被其他用戶的事務所幹擾,多個併發事務之間數據要相互隔離。
  持久性:指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,即時數據庫發生故障也不應該對其有任何影響。

二、Spring事務管理高層抽象主要包括3個接口

  1、Platform TransactionManager 事務管理器(提交、回滾事務)
     Spring爲不同的持久化框架提供了不同的Platform TransactionManager接口實現。如:
        使用Spring JDBC或iBatis進行持久化數據時使用DataSourceTransactionManager
        使用Hibernate3.0版本進行持久化數據時使用HibernateTransactionManager
  2、TransactionDefinition 事務定義信息(隔離、傳播、超時、只讀)
        髒讀:一個事務讀取了另一個事務改寫但還未提交的數據,如果這些數據被回滾,則讀到的數據是無效的。
        不可重複讀:在同一事務中,多次讀取同一數據返回的結果有所不同。
        幻讀:一個事務讀取了幾行記錄後,另一個事務插入一些記錄,幻讀就發生了。再後來的查詢中,第一個事務就會發現有些原來沒有的記錄。
        事務隔離級別:(五種)
  •     DEFAULT--使用後端數據庫默認的隔離級別(Spring中的選擇項)
  •     READ_UNCOMMITED--允許你讀取還未提交的改變了的數據。可能導致髒、幻、不可重複讀
  •     READ_COMMITTED--允許在併發事務已經提交後讀取。可防止髒讀,但幻讀和不可重複讀仍可發生
  •     REPEATABLE_READ--對相同字段的多次讀取是一致的,除非數據被事務本身改變。可防止髒、不可重複讀,但幻讀仍可能發生
  •     SERIALIZABLE--完全服從ACID的隔離級別,確保不發生髒、幻、不可重複讀。這在所有的隔離級別中是最慢的,它是典型的通過完全鎖定在事務中涉及的數據表來完成的
    其中,MySQL默認採用REPEATABLE_READ隔離級別;Oracle默認採用READ_COMMITTED隔離級別

        事務傳播行爲:(七種)
  •     REQUIRED--支持當前事務,如果當前沒有事務,就新建一個事務。這是最常見的選擇。
  •     SUPPORTS--支持當前事務,如果當前沒有事務,就以非事務方式執行。
  •     MANDATORY--支持當前事務,如果當前沒有事務,就拋出異常。
  •     REQUIRES_NEW--新建事務,如果當前存在事務,把當前事務掛起。
  •     NOT_SUPPORTED--以非事務方式執行操作,如果當前存在事務,就把當前事務掛起。
  •     NEVER--以非事務方式執行,如果當前存在事務,則拋出異常。
  •     NESTED--如果當前存在事務,則在嵌套事務內執行。如果當前沒有事務,則進行與REQUIRED類似的操作。擁有多個可以回滾的保存點,內部回滾不會對外部事務產生影響。只對DataSourceTransactionManager有效
 3、TransactionStatus 事務具體運行狀態

三、Spring提供了以下方法控制事務

  1、編程式事務管理(基於Java編程控制,很少使用)
       利用TransactionTemplate將多個DAO操作封裝起來
  2、聲明式事務管理(基於Spring的AOP配置控制)
       -基於TransactionProxyFactoryBean的方式.(很少使用)
            需要爲每個進行事務管理的類,配置一個TransactionProxyFactoryBean進行增強.
       a.基於XML配置(經常使用)
            一旦配置好之後,類上不需要添加任何東西。
            如果Action作爲目標對象切入事務,需要在<aop:config>元素裏添加proxy-target-class="true"屬性。原因是通知Spring框架採用CGLIB技術生成具有事務管理功能的Action類。
       b.基於註解(配置簡單,經常使用)
            在applicationContext.xml中開啓事務註解配置。(applicationContext.xml中只需定義Bean並追加以下元素)
<bean id="txManager" class="...">
  <property name="sessionFactory">
  </property>
<tx:annotation-driven transaction-manager="txManager"/>

            在目標組件類中使用@Transactional,該標記可定義在類前或方法前。

四、示例(銀行轉賬)

      1、編程式

  1. /** 
  2.  * @Description:轉賬案例的DAO層接口 
  3.  *  
  4.  */  
  5. public interface AccountDao {  
  6.     /** 
  7.      * @param out 
  8.      *            :轉出賬號 
  9.      * @param money 
  10.      *            :轉賬金額 
  11.      */  
  12.     public void outMoney(String out, Double money);  
  13.   
  14.     /** 
  15.      *  
  16.      * @param in 
  17.      *            :轉入賬號 
  18.      * @param money 
  19.      *            :轉賬金額 
  20.      */  
  21.     public void inMoney(String in, Double money);  
  22. }  

  1. /** 
  2.  * @Description:轉賬案例的DAO層實現類 
  3.  */  
  4. public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {  
  5.     /** 
  6.      * @param out 
  7.      *            :轉出賬號 
  8.      * @param money 
  9.      *            :轉賬金額 
  10.      */  
  11.     @Override  
  12.     public void outMoney(String out, Double money) {  
  13.         String sql = "update account set money = money-? where name = ?";  
  14.         this.getJdbcTemplate().update(sql, money, out);  
  15.     }  
  16.     /** 
  17.      * @param in 
  18.      *            :轉入賬號 
  19.      * @param money 
  20.      *            :轉賬金額 
  21.      */  
  22.     @Override  
  23.     public void inMoney(String in, Double money) {  
  24.         String sql = "update account set money = money+? where name = ?";  
  25.         this.getJdbcTemplate().update(sql, money, in);  
  26.     }  
  27. }  

  1. /** 
  2.  * @Description:轉賬案例的業務接口 
  3.  * 
  4.  */  
  5. public interface AccountService {  
  6.     /** 
  7.      * @param out   :轉出賬號 
  8.      * @param in    :轉入賬號 
  9.      * @param money :轉賬金額 
  10.      */  
  11.     public void transfer(String out,String in,Double money);  
  12. }  

  1. /** 
  2.  * @Description:轉賬案例的業務層實現類 
  3.  */  
  4. public class AccountServiceImpl implements AccountService {  
  5.     // 注入轉賬的DAO  
  6.     private AccountDao accountDao;  
  7.   
  8.     // 注入事務管理的模板  
  9.     private TransactionTemplate transactionTemplate;  
  10.   
  11.     /** 
  12.      * @param out 
  13.      *            :轉出賬號 
  14.      * @param in 
  15.      *            :轉入賬號 
  16.      * @param money 
  17.      *            :轉賬金額 
  18.      */  
  19.     @Override  
  20.     public void transfer(final String out, final String in, final Double money) {  
  21.   
  22.         // 未經事務控制的業務處理操作,如果過程中出異常,則導致前面的操作能完成,後面的不能,即轉賬成功但未收到轉賬款  
  23.         // accountDao.outMoney(out, money);  
  24.         // int i = 1/0;  
  25.         // accountDao.inMoney(in, money);  
  26.   
  27.         transactionTemplate.execute(new TransactionCallbackWithoutResult() {  
  28.   
  29.             @Override  
  30.             protected void doInTransactionWithoutResult(  
  31.                     TransactionStatus transactionStatus) {  
  32.                 accountDao.outMoney(out, money);  
  33.                 // int i = 1 / 0;//事務控制,即出現異常,該段內代碼都執行失效  
  34.                 accountDao.inMoney(in, money);  
  35.             }  
  36.         });  
  37.     }  
  38.   
  39.     public void setAccountDao(AccountDao accountDao) {  
  40.         this.accountDao = accountDao;  
  41.     }  
  42.   
  43.     public void setTransactionTemplate(TransactionTemplate transactionTemplate) {  
  44.         this.transactionTemplate = transactionTemplate;  
  45.     }  
  46. }  

applicationContext1.xml

  1. <!-- 引入外部的屬性文件 -->  
  2.     <context:property-placeholder location="classpath:jdbc.properties"/>  
  3.       
  4.     <!-- 配置c3p0連接池 -->  
  5.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
  6.         <property name="driverClass" value="${jdbc.driverClass}" />  
  7.         <property name="jdbcUrl" value="${jdbc.url}" />  
  8.         <property name="user" value="${jdbc.username}" />  
  9.         <property name="password" value="${jdbc.password}" />  
  10.     </bean>  
  11.       
  12.     <!-- 配置業務層類 -->  
  13.     <bean id="accountService" class="com.zs.spring.demo1.AccountServiceImpl">  
  14.         <property name="accountDao" ref="accountDao" />  
  15.         <!-- 注入事務管理的模板 -->  
  16.         <property name="transactionTemplate" ref="transactionTemplate" />  
  17.     </bean>  
  18.       
  19.     <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->  
  20.     <bean id="accountDao" class="com.zs.spring.demo1.AccountDaoImpl">  
  21.         <property name="dataSource" ref="dataSource" />  
  22.     </bean>  
  23.       
  24.     <!-- 配置DAO類(未簡化) -->  
  25.     <!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">  
  26.         <property name="dataSource" ref="dataSource" />  
  27.     </bean>  
  28.     <bean id="accountDao" class="com.zs.spring.demo1.AccountDaoImpl">  
  29.         <property name="jdbcTemplate" ref="jdbcTemplate" />  
  30.     </bean> -->  
  31.       
  32.     <!-- ==================================1.編程式的事務管理=============================================== -->  
  33.     <!-- 配置事務管理器 -->  
  34.     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  35.         <property name="dataSource" ref="dataSource" />  
  36.     </bean>  
  37.       
  38.     <!-- 配置事務管理的模板:Spring爲了簡化事務管理的代碼而提供的類 -->  
  39.     <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">  
  40.         <property name="transactionManager" ref="transactionManager"/>  
  41.     </bean>  

測試:

  1. @RunWith(SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration("classpath:applicationContext1.xml")  
  3. public class TransactionTest {  
  4.     @Resource(name = "accountService")  
  5.     private AccountService accountService;  
  6.   
  7.     @Test  
  8.     public void demo1() {  
  9.         accountService.transfer("aaa""bbb", 200d);  
  10.     }  
  11. }  

    2、基於TransactionProxyFactoryBean的方式

  1. public class AccountServiceImpl implements AccountService {  
  2.     // 注入轉賬的DAO  
  3.     private AccountDao accountDao;  
  4.   
  5.     /** 
  6.      * @param out 
  7.      *            :轉出賬號 
  8.      * @param in 
  9.      *            :轉入賬號 
  10.      * @param money 
  11.      *            :轉賬金額 
  12.      */  
  13.     @Override  
  14.     public void transfer(String out, String in, Double money) {  
  15.         accountDao.outMoney(out, money);  
  16.         // int i = 1/0;  
  17.         accountDao.inMoney(in, money);  
  18.     }  
  19.   
  20.     public void setAccountDao(AccountDao accountDao) {  
  21.         this.accountDao = accountDao;  
  22.     }  
  23. }  

applicationContext2.xml

  1. <!-- 引入外部的屬性文件 -->  
  2.     <context:property-placeholder location="classpath:jdbc.properties"/>  
  3.       
  4.     <!-- 配置c3p0連接池 -->  
  5.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
  6.         <property name="driverClass" value="${jdbc.driverClass}" />  
  7.         <property name="jdbcUrl" value="${jdbc.url}" />  
  8.         <property name="user" value="${jdbc.username}" />  
  9.         <property name="password" value="${jdbc.password}" />  
  10.     </bean>  
  11.       
  12.     <!-- 配置業務層類 -->  
  13.     <bean id="accountService" class="com.zs.spring.demo2.AccountServiceImpl">  
  14.         <property name="accountDao" ref="accountDao" />  
  15.     </bean>  
  16.       
  17.     <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->  
  18.     <bean id="accountDao" class="com.zs.spring.demo2.AccountDaoImpl">  
  19.         <property name="dataSource" ref="dataSource" />  
  20.     </bean>  
  21.       
  22.     <!-- ==================================2.使用XML配置聲明式的事務管理(原始方式)=============================================== -->  
  23.       
  24.     <!-- 配置事務管理器 -->  
  25.     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  26.         <property name="dataSource" ref="dataSource" />  
  27.     </bean>  
  28.       
  29.     <!-- 配置業務層的代理 -->  
  30.     <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  
  31.         <!-- 配置目標對象 -->  
  32.         <property name="target" ref="accountService" />  
  33.         <!-- 注入事務管理器 -->  
  34.         <property name="transactionManager" ref="transactionManager"></property>  
  35.         <!-- 注入事務的屬性 -->  
  36.         <property name="transactionAttributes">  
  37.             <props>  
  38.                 <!--   
  39.                     prop的格式:  
  40.                         * PROPAGATION   :事務的傳播行爲  
  41.                         * ISOTATION     :事務的隔離級別  
  42.                         * readOnly      :只讀  
  43.                         * -EXCEPTION    :發生哪些異常回滾事務  
  44.                         * +EXCEPTION    :發生哪些異常不回滾事務  
  45.                  -->  
  46.                 <prop key="transfer">PROPAGATION_REQUIRED</prop>  
  47.                 <!-- <prop key="transfer">PROPAGATION_REQUIRED,readOnly</prop> -->  
  48.                 <!-- <prop key="transfer">PROPAGATION_REQUIRED,+java.lang.ArithmeticException</prop> -->  
  49.             </props>  
  50.         </property>  
  51.     </bean>  

測試:

  1. @RunWith(SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration("classpath:applicationContext2.xml")  
  3. public class TransactionTest {  
  4.     /** 
  5.      * 一定要注入代理類:因爲代理類進行增強的操作 
  6.      */  
  7.     // @Resource(name="accountService")  
  8.     @Resource(name = "accountServiceProxy")  
  9.     private AccountService accountService;  
  10.   
  11.     @Test  
  12.     public void demo1() {  
  13.         accountService.transfer("aaa""bbb", 200d);  
  14.     }  
  15. }  

    3、基於XML配置

  1. public class AccountServiceImpl implements AccountService {  
  2.     // 注入轉賬的DAO  
  3.     private AccountDao accountDao;  
  4.   
  5.     /** 
  6.      * @param out 
  7.      *            :轉出賬號 
  8.      * @param in 
  9.      *            :轉入賬號 
  10.      * @param money 
  11.      *            :轉賬金額 
  12.      */  
  13.     @Override  
  14.     public void transfer(String out, String in, Double money) {  
  15.         accountDao.outMoney(out, money);  
  16.         // int i = 1/0;  
  17.         accountDao.inMoney(in, money);  
  18.   
  19.     }  
  20.   
  21.     public void setAccountDao(AccountDao accountDao) {  
  22.         this.accountDao = accountDao;  
  23.     }  
  24. }  

applicationContext3.xml

  1. <!-- 引入外部的屬性文件 -->  
  2.     <context:property-placeholder location="classpath:jdbc.properties"/>  
  3.       
  4.     <!-- 配置c3p0連接池 -->  
  5.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
  6.         <property name="driverClass" value="${jdbc.driverClass}" />  
  7.         <property name="jdbcUrl" value="${jdbc.url}" />  
  8.         <property name="user" value="${jdbc.username}" />  
  9.         <property name="password" value="${jdbc.password}" />  
  10.     </bean>  
  11.       
  12.     <!-- 配置業務層類 -->  
  13.     <bean id="accountService" class="com.zs.spring.demo3.AccountServiceImpl">  
  14.         <property name="accountDao" ref="accountDao" />  
  15.     </bean>  
  16.       
  17.     <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->  
  18.     <bean id="accountDao" class="com.zs.spring.demo3.AccountDaoImpl">  
  19.         <property name="dataSource" ref="dataSource" />  
  20.     </bean>  
  21.       
  22.     <!-- ==================================3.使用XML配置聲明式的事務管理,基於tx/aop=============================================== -->  
  23.       
  24.     <!-- 配置事務管理器 -->  
  25.     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  26.         <property name="dataSource" ref="dataSource" />  
  27.     </bean>  
  28.       
  29.     <!-- 配置事務的通知 -->  
  30.     <tx:advice id="txAdvice" transaction-manager="transactionManager">  
  31.         <tx:attributes>  
  32.             <!--   
  33.                 propagation :事務傳播行爲  
  34.                 isolation   :事務的隔離級別  
  35.                 read-only   :只讀  
  36.                 rollback-for:發生哪些異常回滾  
  37.                 no-rollback-for :發生哪些異常不回滾  
  38.                 timeout     :過期信息  
  39.              -->  
  40.             <tx:method name="transfer" propagation="REQUIRED"/>  
  41.         </tx:attributes>  
  42.     </tx:advice>  
  43.       
  44.     <!-- 配置切面 -->  
  45.     <aop:config>  
  46.         <!-- 配置切入點 -->  
  47.         <aop:pointcut expression="execution(* com.zs.spring.demo3.AccountService+.*(..))" id="pointcut1"/>  
  48.         <!-- 配置切面 -->  
  49.         <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>  
  50.     </aop:config>  

測試:

  1. /** 
  2.  * @Description:Spring的聲明式事務管理的方式二:基於AspectJ的XML方式的配置 
  3.  */  
  4. @RunWith(SpringJUnit4ClassRunner.class)  
  5. @ContextConfiguration("classpath:applicationContext3.xml")  
  6. public class TransactionTest {  
  7.     /** 
  8.      * 一定要注入代理類:因爲代理類進行增強的操作 
  9.      */  
  10.     @Resource(name = "accountService")  
  11.     private AccountService accountService;  
  12.   
  13.     @Test  
  14.     public void demo1() {  
  15.         accountService.transfer("aaa""bbb", 200d);  
  16.     }  
  17. }  

   4、基於註解

  1. /** 
  2.  * @Transactional中的的屬性 propagation :事務的傳播行爲 isolation :事務的隔離級別 readOnly :只讀 
  3.  *                     rollbackFor :發生哪些異常回滾 noRollbackFor :發生哪些異常不回滾 
  4.  *                     rollbackForClassName 根據異常類名回滾 
  5.  */  
  6. @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, readOnly = false)  
  7. public class AccountServiceImpl implements AccountService {  
  8.     // 注入轉賬的DAO  
  9.     private AccountDao accountDao;  
  10.   
  11.     /** 
  12.      * @param out 
  13.      *            :轉出賬號 
  14.      * @param in 
  15.      *            :轉入賬號 
  16.      * @param money 
  17.      *            :轉賬金額 
  18.      */  
  19.     @Override  
  20.     public void transfer(String out, String in, Double money) {  
  21.         accountDao.outMoney(out, money);  
  22.         // int i = 1/0;  
  23.         accountDao.inMoney(in, money);  
  24.     }  
  25.   
  26.     public void setAccountDao(AccountDao accountDao) {  
  27.         this.accountDao = accountDao;  
  28.     }  
  29. }  

applicationContext4.xml

  1. <!-- 引入外部的屬性文件 -->  
  2.     <context:property-placeholder location="classpath:jdbc.properties"/>  
  3.       
  4.     <!-- 配置c3p0連接池 -->  
  5.     <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">  
  6.         <property name="driverClass" value="${jdbc.driverClass}" />  
  7.         <property name="jdbcUrl" value="${jdbc.url}" />  
  8.         <property name="user" value="${jdbc.username}" />  
  9.         <property name="password" value="${jdbc.password}" />  
  10.     </bean>  
  11.       
  12.     <!-- 配置業務層類 -->  
  13.     <bean id="accountService" class="com.zs.spring.demo4.AccountServiceImpl">  
  14.         <property name="accountDao" ref="accountDao" />  
  15.     </bean>  
  16.       
  17.     <!-- 配置DAO類(簡化,會自動配置JdbcTemplate) -->  
  18.     <bean id="accountDao" class="com.zs.spring.demo4.AccountDaoImpl">  
  19.         <property name="dataSource" ref="dataSource" />  
  20.     </bean>  
  21.       
  22.     <!-- ==================================4.使用註解配置聲明式事務============================================ -->  
  23.   
  24.     <!-- 配置事務管理器 -->  
  25.     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  26.         <property name="dataSource" ref="dataSource" />  
  27.     </bean>  
  28.       
  29.     <!-- 開啓註解事務 -->  
  30.     <tx:annotation-driven transaction-manager="transactionManager"/>  
  31.       

測試:

  1. @RunWith(SpringJUnit4ClassRunner.class)  
  2. @ContextConfiguration("classpath:applicationContext4.xml")  
  3. public class TransactionTest {  
  4.   
  5.     /** 
  6.      * 一定要注入代理類:因爲代理類進行增強的操作 
  7.      */  
  8.     @Resource(name = "accountService")  
  9.     private AccountService accountService;  
  10.   
  11.     @Test  
  12.     public void demo1() {  
  13.         accountService.transfer("aaa""bbb", 200d);  
  14.     }  

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