Spring事務詳解

Spring事務配置:
1.在Spring配置文件引入tx

<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-3.0.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd">

2.配置基於註解的聲明式事務管理
spring不負責事務的處理,把事務交給各事務處理器

<!-- 配置聲明式事務管理器-->  
<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
    <property name="sessionFactory" ref="sessionFactory"></property>  
</bean>  
<!-- 註解驅動-->  
<tx:annotation-driven transaction-manager="txManager"/>

3.在要使用事務管理的類或者方法上增加代碼@Transactional,Spring官方團隊建議不要在接口使用。在類上使用@Transactional,類中的所有public方法都將使用事務

@Transactional
public class Txtest implements TestService { }

在public方法上使用@Transactional,則該方法使用事務;非public方法使用@Transactional不會報錯,但也不會使用事務,相當於“白做”
如果在類上使用@Transactional,但是類中的某個方法不想使用事務,則可以使用

@Transactional
public class Txtest implements TestService {   

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public List<Object> getAll() {
        return null;
    }   
}

使用tx標籤配置的攔截器
1.配置sessionFactory

<!-- 配置SessionFactory -->
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="configLocation">
        <value>classpath:hibernate.cfg.xml</value>
    </property>
</bean>

2.配置事務管理器

<!-- 事務配置 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

3.配置事務的傳播性

<!-- 使用聲明方式配置事務 -->
<tx:advice id="TxAdvice" transaction-manager="transactionManager">
     <tx:attributes>
           <tx:method name="query*" read-only="true"/>
           <tx:method name="insert*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
           <tx:method name="update*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
           <tx:method name="delete*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
           <tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
    </tx:attributes>
</tx:advice>

4.AOP配置參與事務的類

<aop:config>
    <aop:pointcut id="allServiceMethod" expression="execution(* com.chillax.*.service.*.*(..))"/>
    <aop:advisor pointcut-ref="allServiceMethod" advice-ref="TxAdvice" />
</aop:config>

事務的傳播性
1)@Transactional(propagation=Propagation.REQUIRED):默認的spring事務傳播級別,使用該級別的特點是,如果上下文中已經存在事務,那麼就加入到事務中執行,如果當前上下文中不存在事務,則新建事務執行,所以這個級別通常能滿足處理大多數的業務場景。
2)@Transactional(propagation=PROPAGATION_SUPPORTS):從字面意思就知道,supports(支持),該傳播級別的特點是,如果上下文存在事務,則支持當前事務,加入到事務執行,如果沒有事務,則使用非事務的方式執行。所以說,並非所有的包在transactionTemplate.execute中的代碼都會有事務支持。這個通常是用來處理那些並非原子性的非核心業務邏輯操作,應用場景較少。
3)@Transactional(propagation=PROPAGATION_MANDATORY):該級別的事務要求上下文中必須要存在事務,否則就會拋出異常!配置該方式的傳播級別是有效的控制上下文調用代碼遺漏添加事務控制的保證手段。比如一段代碼不能單獨被調用執行,但是一旦被調用,就必須有事務包含的情況,就可以使用這個傳播級別。
4)@Transactional(propagation=PROPAGATION_REQUIRES_NEW):從字面即可知道,每次都要一個新的事務,該傳播級別的特點是,每次都會新建一個事務,並且同時將上下文中的事務掛起,當新建事務執行完成以後,上下文事務再恢復執行。
這是一個很有用的傳播級別,舉一個應用場景:現在有一個發送100個紅包的操作,在發送之前,要做一些系統的初始化、驗證、數據記錄操作,然後發送100封紅包,然後再記錄發送日誌,發送日誌要求100%的準確,如果日誌不準確,那麼整個父事務邏輯需要回滾。
怎麼處理整個業務需求呢?就是通過這個PROPAGATION_REQUIRES_NEW級別的事務傳播控制就可以完成。發送紅包的子事務不會直接影響到父事務的提交和回滾。
5)@Transactional(propagation=PROPAGATION_NOT_SUPPORTED):這個也可以從字面得知not supported(不支持),當前級別的特點是,如果上下文中存在事務,
則掛起事務,執行當前邏輯,結束後恢復上下文的事務。
這個級別有什麼好處?可以幫助你將事務極可能的縮小。我們知道一個事務越大,它存在的風險也就越多。所以在處理事務的過程中,要保證儘可能的縮小範圍。比如一段代碼,是每次邏輯操作都必須調用的,比如循環1000次的某個非核心業務邏輯操作。這樣的代碼如果包在事務中,勢必造成事務太大,導致出現一些難以考慮周全的異常情況。所以這個事務這個級別的傳播級別就派上用場了,用當前級別的事務模板抱起來就可以了。
6)@Transactional(propagation=PROPAGATION_NEVER):該事務更嚴格,上面一個事務傳播級別只是不支持而已,有事務就掛起,而PROPAGATION_NEVER傳播級別要求上下文中不能存在事務,一旦有事務,就拋出runtime異常,強制停止執行!
7)@Transactional(propagation=PROPAGATION_NESTED):字面也可知道,nested,嵌套級別事務。該傳播級別特徵是,如果上下文中存在事務,則嵌套事務執行,如果不存在事務,則新建事務。

事務的隔離級別
1)@Transactional(isolation = Isolation.SERIALIZABLE):最嚴格的級別,事務串行執行,資源消耗最大;
2)@Transactional(isolation = Isolation.REPEATABLE_READ):保證了一個事務不會修改已經由另一個事務讀取但未提交(回滾)的數據。避免了“髒讀取”和“不可重複讀取”的情況,但是帶來了更多的性能損失。
3)@Transactional(isolation = Isolation.READ_COMMITTED):大多數主流數據庫的默認事務等級,保證了一個事務不會讀到另一個並行事務已修改但未提交的數據,避免了“髒讀取”,該級別適用於大多數系統。
4)@Transactional(isolation = Isolation.READ_UNCOMMITTED):保證了讀取過程中不會讀取到非法數據。

1:Dirty reads—讀髒數據。也就是說,比如事務A的未提交(還依然緩存)的數據被事務B讀走,如果事務A失敗回滾,會導致事務B所讀取的的數據是錯誤的。
2:non-repeatable reads—不可重複讀。比如事務A中兩處讀取數據-total-的值。在第一讀的時候,total是100,然後事務B就把total的數據改成200,事務A再讀一次,結果就發現,total竟然就變成200了,造成事務A數據混亂。
3:phantom reads—幻象讀數據。這個和non-repeatable reads相似,也是同一個事務中多次讀不一致的問題。但是non-repeatable reads的不一致是因爲他所要取的數據集被改變了(比如total的數據),但是phantom reads所要讀的數據的不一致卻不是他所要讀的數據集改變,而是他的條件數據集改變。比如Select account.id where account.name=”grace”,第一次讀去了6個符合條件的id,第二次讀取的時候,由於事務b把一個帳號的名字由”dd”改成”grace”,結果取出來了7個數據。
不可重複讀的重點是修改:同樣的條件, 你讀取過的數據, 再次讀取出來發現值不一樣了。
幻讀的重點在於新增或者刪除:同樣的條件, 第1次和第2次讀出來的記錄數不一樣。

常用數據庫默認事務隔離級別
MYSQL:默認爲REPEATABLE_READ
SQLSERVER:默認爲READ_COMMITTED
ORACLE:默認爲READ_COMMITTED

@Transactional註解中常用參數說明
readOnly:該屬性用於設置當前事務是否爲只讀事務,設置爲true表示只讀,false則表示可讀寫,默認值爲false。例如:@Transactional(readOnly=true)
rollbackFor:該屬性用於設置需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。例如:
指定單一異常類:@Transactional(rollbackFor=RuntimeException.class);
指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
rollbackForClassName:該屬性用於設置需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,則進行事務回滾。例如:
指定單一異常類名稱:@Transactional(rollbackForClassName=”RuntimeException”);
指定多個異常類名稱:@Transactional(rollbackForClassName={“RuntimeException”,”Exception”})
noRollbackFor:該屬性用於設置不需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,不進行事務回滾。例如:
指定單一異常類:@Transactional(noRollbackFor=RuntimeException.class);
指定多個異常類:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})
noRollbackForClassName:該屬性用於設置不需要進行回滾的異常類名稱數組,當方法中拋出指定異常名稱數組中的異常時,不進行事務回滾。例如:
指定單一異常類名稱:@Transactional(noRollbackForClassName=”RuntimeException”);
指定多個異常類名:@Transactional(noRollbackForClassName={“RuntimeException”,”Exception”})
propagation:該屬性用於設置事務的傳播行爲,具體取值可參考上文。例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)
isolation:該屬性用於設置底層數據庫的事務隔離級別,事務隔離級別用於處理多事務併發的情況,通常使用數據庫的默認隔離級別即可,基本不需要進行設置
timeout:該屬性用於設置事務的超時秒數,默認值爲-1表示永不超時

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