1. 程式人生 > >Spring中JDBC中 宣告式事務管理(DataSourceTransactionManager)

Spring中JDBC中 宣告式事務管理(DataSourceTransactionManager)

參考

https://www.cnblogs.com/sonng/p/6587139.html
在一個業務的實現過程中,可能需要多條sql完成對資料庫的操作,比如賬戶登入,需要匹配使用者名稱和密碼,然後要增加積分,還要記錄登入的ip和時間,這可能需要三個sql語句,這三個語句應當是一個整體,任意一個sql執行不成功,都表示這個業務沒有執行完成,這就有了事務的概念。

事務是資料庫中的概念,就是對資料庫的一組操作,由一條或多條sql組成。

事務具有同步的特點,一條sql執行失敗,其他sql都不會執行,即要麼都執行,要麼都不執行。

用START TRANSACTION開啟一個事務,這之後執行的sql語句,在用COMMIT提交事務之前,都沒有被”寫死”到資料庫中,可以用ROLLBACK進行回滾操作。

Spring在jdbc中提供了一個事務管理元件:org.springframework.jdbc.datasource.DataSourceTransactionManager

<!-- 配置事務管理元件 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dbcp">  <!-- dbcp是連線池元件(org.apache.commons.dbcp2.BasicDataSource)的bean -->
</bean>

使用事務管理的功能,跟建立bean一樣,可以採用註解和xml配置兩種方式。當然可能還有別的方式,還沒學到

<!-- 採用註解方式:有原始碼的情況下,將註解加在方法上 -->
<!-- 開啟事務註解標記@Transactional,當呼叫帶@Transactional標記的方法時,將txManager的事務管理功能切入進去 -->
<tx:annotation-driven transactional-manager="txManager" />
<!-- 在需要事務管理的方法上加上@Transactional註解即可 -->
<!-- 採用xml配置的方式:使用別人寫好的功能,沒有原始碼,就可以用xml配置 -->
<tx:advice id="txAdvice" transaction-manager="txManager" >   <!-- 仍然使用txManager作為事務管理元件 -->
    <tx:attributes>
        <tx:method name="updateTitleAndBody" />   <!-- 在哪些方法上新增事務管理 -->
        <tx:method name="register" />             <!-- 這裡寫方法名 -->
        <tx:method name="checkLogin" />           <!-- 支援萬用字元 -->
        <tx:method name="listNotebook" />
        <tx:method name="getDeletedNotes" />
    </tx:attributes>
</tx:advice>
<!-- 通過aop機制完成事務管理 -->
<aop:config>                                      <!-- 作用在哪些元件上 -->
    <aop:pointcut id="target" expression="within(net.sonng.note.service.UserServiceImpl)" />
                                                  <!-- 這個expression的寫法有講究 -->
    <aop:advisor advice-ref="txAdvice"  pointcut-ref="target"/> 
</aop:config>

其他:MyBatis應該也提供了事務管理的元件

參考

<?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:tx="http://www.springframework.org/schema/tx"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans   
         http://www.springframework.org/schema/beans/spring-beans-2.0.xsd   
         http://www.springframework.org/schema/tx   
         http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">  

第二步:在spring的配置檔案中修改,將所有具有@Transactional 註解的bean自動配置為宣告式事務支援

 <!--JDBC事務管理器,根據你的情況使用不同的事務管理器,如果工程中有Hibernate,就用Hibernate的事務管理器 -->     
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">   
             <property name="dataSource">   
                 <ref local="dataSource"/>   
             </property>   
    </bean>      

    <!-- 用註解來實現事務管理 -->   
    <tx:annotation-driven transaction-manager="transactionManager"/>  

第三步: 在介面或類的宣告處 ,寫一個@Transactional. 要是隻的介面上寫, 介面的實現類就會繼承下來.

介面的實現類的具體方法,還可以覆蓋類宣告處的設定.

 @Transactional  
    public class TestPOAOImpl extends POAOBase implements TestPOAO   
    {      
        @Transactional(isolation = Isolation.READ_COMMITTED)   
        public void test1()   
         {   
             String sql = "INSERT INTO sy_test (NAME,AGE) VALUES('註解趙雲',30)";   
             execute(sql);   

             sql = "INSERT INTO sy_test (NAME,AGE) VALUES('註解張飛',26)";   
             execute(sql);   

            int a = 9 / 0; //異常   

             sql = "INSERT INTO sy_test (NAME,AGE) VALUES('註解關羽',33)";   
             execute(sql);   
             System.out.println("走完了");   
         }   
    //execute() 方法略...   
    }  

注意的幾點:

  • @Transactional只能被應用到public方法上,對於其它非public的方法,如果標記了@Transactional也不會報錯,但方法沒有事務功能.
  • 預設情況下,一個有事務方法, 遇到RuntiomeException 時會回滾 . 遇到 受檢查的異常 是不會回滾 的. 要想所有異常都回滾,要加上 @Transactional( rollbackFor={Exception.class,其它異常}) .

@Transactional 的所有可選屬性如下:
這裡寫圖片描述
事務的隔離級別 有如下可選:
可以去看spring原始碼 : org.springframework.transaction.annotation.Isolation

  • DEFAULT採用資料庫預設隔離級別
  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

資料庫提供了四種事務隔離級別, 不同的隔離級別採用不同的鎖類開來實現.
在四種隔離級別中, Serializable的級別最高, Read Uncommited級別最低.
大多數資料庫的預設隔離級別為: Read Commited,如Sql Server , Oracle.
少數資料庫預設的隔離級別為Repeatable Read, 如MySQL InnoDB儲存引擎
即使是最低的級別,也不會出現 第一類 丟失 更新問題 .

  • Read Uncommited :讀未提交資料( 會出現髒讀,不可重複讀,幻讀 ,避免了 第一類丟失 更新 )
  • Read Commited :讀已提交的資料(會出現不可重複讀,幻讀)
  • Repeatable Read :可重複讀(會出現幻讀)
  • Serializable :序列化

丟失 更新 :
當兩個或多個事務選擇同一行,然後基於最初選定的值更新該行時,會發生丟失更新問題。每個事務都不知道其它事務的存在。最後的更新將重寫由其它事務所做的更新,這將導致資料丟失。   
例:
事務A和事務B同時修改某行的值,
1.事務A將數值改為1並提交
2.事務B將數值改為2並提交。
這時資料的值為2,事務A所做的更新將會丟失。
解決辦法:對行加鎖,只允許併發一個更新事務。

髒讀: 一個事務讀到另一個事務未提交的更新資料

例:
1.Mary的原工資為1000, 財務人員將Mary的工資改為了8000(但未提交事務)
2.Mary讀取自己的工資 ,發現自己的工資變為了8000,歡天喜地!
3.而財務發現操作有誤,回滾了事務,Mary的工資又變為了1000, 像這樣,Mary記取的工資數8000是一個髒資料。

不可重複讀: 在同一個事務中,多次讀取同一資料,返回的結果有所不同. 換句話說就是,後續讀取可以讀到另一個事務已提交的更新資料. 相反”可重複讀”在同一事務多次讀取資料時,能夠保證所讀資料一樣,也就是後續讀取不能讀到另一事務已提交的更新資料。

例:
1.在事務1中,Mary 讀取了自己的工資為1000,操作並沒有完成
2.在事務2中,這時財務人員修改了Mary的工資為2000,並提交了事務
3.在事務1中,Mary 再次讀取自己的工資時,工資變為了2000
解決辦法:如果只有在修改事務完全提交之後才可以讀取資料,則可以避免該問題。

幻讀: 一個事務讀取到另一個事務已提交的insert資料.
例:
第一個事務對一個表中的資料進行了修改,這種修改涉及到表中的全部資料行。同時 (此時第一事務還未提交) ,第二個事務向表中插入一行新資料。這時第一個事務再去讀取表時,發現表中還有沒有修改的資料行,就好象發生了幻覺一樣。

事務的傳播屬性 ,有如下可選
可以去看spring原始碼 : org.springframework.transaction.annotation.Propagation
這裡寫圖片描述