Spring源碼剖析8:Spring事務概述

原文出處: 張開濤html

數據庫事務概述

事務首先是一系列操做組成的工做單元,該工做單元內的操做是不可分割的,即要麼全部操做都作,要麼全部操做都不作,這就是事務。java

事務必需知足ACID(原子性、一致性、隔離性和持久性)特性,缺一不可:程序員

  • 原子性(Atomicity):即事務是不可分割的最小工做單元,事務內的操做要麼全作,要麼全不作;
  • 一致性(Consistency):在事務執行前數據庫的數據處於正確的狀態,而事務執行完成後數據庫的數據仍是處於正確的狀態,即數據完整性約束沒有被破壞;如銀行轉賬,A轉賬給B,必須保證A的錢必定轉給B,必定不會出現A的錢轉了但B沒收到,不然數據庫的數據就處於不一致(不正確)的狀態。
  • 隔離性(Isolation):併發事務執行之間無影響,在一個事務內部的操做對其餘事務是不產生影響,這須要事務隔離級別來指定隔離性;
  • 持久性(Durability):事務一旦執行成功,它對數據庫的數據的改變必須是永久的,不會因好比遇到系統故障或斷電形成數據不一致或丟失。

在實際項目開發中數據庫操做通常都是併發執行的,即有多個事務併發執行,併發執行就可能遇到問題,目前常見的問題以下:面試

  • 丟失更新:兩個事務同時更新一行數據,最後一個事務的更新會覆蓋掉第一個事務的更新,從而致使第一個事務更新的數據丟失,這是因爲沒有加鎖形成的;
  • 髒讀:一個事務看到了另外一個事務未提交的更新數據;
  • 不可重複讀:在同一事務中,屢次讀取同一數據卻返回不一樣的結果;也就是有其餘事務更改了這些數據;
  • 幻讀:一個事務在執行過程當中讀取到了另外一個事務已提交的插入數據;即在第一個事務開始時讀取到一批數據,但此後另外一個事務又插入了新數據並提交,此時第一個事務又讀取這批數據但發現多了一條,即好像發生幻覺同樣。

爲了解決這些併發問題,須要經過數據庫隔離級別來解決,在標準SQL規範中定義了四種隔離級別:spring

  • 未提交讀(Read Uncommitted):最低隔離級別,一個事務能讀取到別的事務未提交的更新數據,很不安全,可能出現丟失更新、髒讀、不可重複讀、幻讀;
  • 提交讀(Read Committed):一個事務能讀取到別的事務提交的更新數據,不能看到未提交的更新數據,不可能可能出現丟失更新、髒讀,但可能出現不可重複讀、幻讀;
  • 可重複讀(Repeatable Read):保證同一事務中前後執行的屢次查詢將返回同一結果,不受其餘事務影響,可能可能出現丟失更新、髒讀、不可重複讀,但可能出現幻讀;
  • 序列化(Serializable):最高隔離級別,不容許事務併發執行,而必須串行化執行,最安全,不可能出現更新、髒讀、不可重複讀、幻讀。

隔離級別越高,數據庫事務併發執行性能越差,能處理的操做越少。所以在實際項目開發中爲了考慮併發性能通常使用提交讀隔離級別,它能避免丟失更新和髒讀,儘管不可重複讀和幻讀不能避免,但能夠在可能出現的場合使用悲觀鎖或樂觀鎖來解決這些問題。sql

事務類型

數據庫事務類型有本地事務和分佈式事務:數據庫

  • 本地事務:就是普通事務,能保證單臺數據庫上的操做的ACID,被限定在一臺數據庫上;
  • 分佈式事務:涉及兩個或多個數據庫源的事務,即跨越多臺同類或異類數據庫的事務(由每臺數據庫的本地事務組成的),分佈式事務旨在保證這些本地事務的全部操做的ACID,使事務能夠跨越多臺數據庫;

Java事務類型有JDBC事務和JTA事務:express

  • JDBC事務:就是數據庫事務類型中的本地事務,經過Connection對象的控制來管理事務;
  • JTA事務:JTA指Java事務API(Java Transaction API),是Java EE數據庫事務規範, JTA只提供了事務管理接口,由應用程序服務器廠商(如WebSphere Application Server)提供實現,JTA事務比JDBC更強大,支持分佈式事務。

Java EE事務類型有本地事務和全局事務:編程

  • 本地事務:使用JDBC編程實現事務;
  • 全局事務:由應用程序服務器提供,使用JTA事務;

按是否經過編程實現事務有聲明式事務和編程式事務;後端

  • 聲明式事務: 經過註解或XML配置文件指定事務信息;
  • 編程式事務:經過編寫代碼實現事務。

Spring提供的事務管理

Spring框架最核心功能之一就是事務管理,並且提供一致的事務管理抽象,這能幫助咱們:

  • 提供一致的編程式事務管理API,無論使用Spring JDBC框架仍是集成第三方框架使用該API進行事務編程;
  • 無侵入式的聲明式事務支持。

Spring支持聲明式事務和編程式事務事務類型。

spring事務特性

spring全部的事務管理策略類都繼承自org.springframework.transaction.PlatformTransactionManager接口

其中TransactionDefinition接口定義如下特性:

事務隔離級別

  隔離級別是指若干個併發的事務之間的隔離程度。TransactionDefinition 接口中定義了五個表示隔離級別的常量:

  • TransactionDefinition.ISOLATION_DEFAULT:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,一般這值就是TransactionDefinition.ISOLATION_READ_COMMITTED。
  • TransactionDefinition.ISOLATION_READ_UNCOMMITTED:該隔離級別表示一個事務能夠讀取另外一個事務修改但尚未提交的數據。該級別不能防止髒讀,不可重複讀和幻讀,所以不多使用該隔離級別。好比PostgreSQL實際上並無此級別。
  • TransactionDefinition.ISOLATION_READ_COMMITTED:該隔離級別表示一個事務只能讀取另外一個事務已經提交的數據。該級別能夠防止髒讀,這也是大多數狀況下的推薦值。
  • TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務在整個過程當中能夠屢次重複執行某個查詢,而且每次返回的記錄都相同。該級別能夠防止髒讀和不可重複讀。
  • TransactionDefinition.ISOLATION_SERIALIZABLE:全部的事務依次逐個執行,這樣事務之間就徹底不可能產生干擾,也就是說,該級別能夠防止髒讀、不可重複讀以及幻讀。可是這將嚴重影響程序的性能。一般狀況下也不會用到該級別。

事務傳播行爲

      所謂事務的傳播行爲是指,若是在開始當前事務以前,一個事務上下文已經存在,此時有若干選項能夠指定一個事務性方法的執行行爲。在TransactionDefinition定義中包括了以下幾個表示傳播行爲的常量:

  • TransactionDefinition.PROPAGATION_REQUIRED:若是當前存在事務,則加入該事務;若是當前沒有事務,則建立一個新的事務。這是默認值。
  • TransactionDefinition.PROPAGATION_REQUIRES_NEW:建立一個新的事務,若是當前存在事務,則把當前事務掛起。
  • TransactionDefinition.PROPAGATION_SUPPORTS:若是當前存在事務,則加入該事務;若是當前沒有事務,則以非事務的方式繼續運行。
  • TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務方式運行,若是當前存在事務,則把當前事務掛起。
  • TransactionDefinition.PROPAGATION_NEVER:以非事務方式運行,若是當前存在事務,則拋出異常。
  • TransactionDefinition.PROPAGATION_MANDATORY:若是當前存在事務,則加入該事務;若是當前沒有事務,則拋出異常。
  • TransactionDefinition.PROPAGATION_NESTED:若是當前存在事務,則建立一個事務做爲當前事務的嵌套事務來運行;若是當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED。

事務超時

      所謂事務超時,就是指一個事務所容許執行的最長時間,若是超過該時間限制但事務尚未完成,則自動回滾事務。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。

  默認設置爲底層事務系統的超時值,若是底層數據庫事務系統沒有設置超時值,那麼就是none,沒有超時限制。

事務只讀屬性

      只讀事務用於客戶代碼只讀但不修改數據的情形,只讀事務用於特定情景下的優化,好比使用Hibernate的時候。

默認爲讀寫事務。

概述

Spring框架支持事務管理的核心是事務管理器抽象,對於不一樣的數據訪問框架(如Hibernate)經過實現策略接口PlatformTransactionManager,從而能支持各類數據訪問框架的事務管理,PlatformTransactionManager接口定義以下:

java代碼:

public interface PlatformTransactionManager {
       TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
       void commit(TransactionStatus status) throws TransactionException;
       void rollback(TransactionStatus status) throws TransactionException;
}
  • getTransaction():返回一個已經激活的事務或建立一個新的事務(根據給定的TransactionDefinition類型參數定義的事務屬性),返回的是TransactionStatus對象表明了當前事務的狀態,其中該方法拋出TransactionException(未檢查異常)表示事務因爲某種緣由失敗。
  • commit():用於提交TransactionStatus參數表明的事務,具體語義請參考Spring Javadoc;
  • rollback():用於回滾TransactionStatus參數表明的事務,具體語義請參考Spring Javadoc。

TransactionDefinition接口定義以下:

java代碼:

public interface TransactionDefinition {
       int getPropagationBehavior();
       int getIsolationLevel();
       int getTimeout();
       boolean isReadOnly();
       String getName();
}
  • getPropagationBehavior():返回定義的事務傳播行爲;
  • getIsolationLevel():返回定義的事務隔離級別;
  • getTimeout():返回定義的事務超時時間;
  • isReadOnly():返回定義的事務是不是隻讀的;
  • getName():返回定義的事務名字。

TransactionStatus接口定義以下:

java代碼:

public interface TransactionStatus extends SavepointManager {
       boolean isNewTransaction();
       boolean hasSavepoint();
       void setRollbackOnly();
       boolean isRollbackOnly();
       void flush();
       boolean isCompleted();
}
  • isNewTransaction():返回當前事務狀態是不是新事務;
  • hasSavepoint():返回當前事務是否有保存點;
  • setRollbackOnly():設置當前事務應該回滾;
  • isRollbackOnly(():返回當前事務是否應該回滾;
  • flush():用於刷新底層會話中的修改到數據庫,通常用於刷新如Hibernate/JPA的會話,可能對如JDBC類型的事務無任何影響;
  • isCompleted():當前事務否已經完成。

內置事務管理器實現

Spring提供了許多內置事務管理器實現:

  • DataSourceTransactionManager:位於org.springframework.jdbc.datasource包中,數據源事務管理器,提供對單個javax.sql.DataSource事務管理,用於Spring JDBC抽象框架、iBATIS或MyBatis框架的事務管理;
  • JdoTransactionManager:位於org.springframework.orm.jdo包中,提供對單個javax.jdo.PersistenceManagerFactory事務管理,用於集成JDO框架時的事務管理;
  • JpaTransactionManager:位於org.springframework.orm.jpa包中,提供對單個javax.persistence.EntityManagerFactory事務支持,用於集成JPA實現框架時的事務管理;
  • HibernateTransactionManager:位於org.springframework.orm.hibernate3包中,提供對單個org.hibernate.SessionFactory事務支持,用於集成Hibernate框架時的事務管理;該事務管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
  • JtaTransactionManager:位於org.springframework.transaction.jta包中,提供對分佈式事務管理的支持,並將事務管理委託給Java EE應用服務器事務管理器;
  • OC4JjtaTransactionManager:位於org.springframework.transaction.jta包中,Spring提供的對OC4J10.1.3+應用服務器事務管理器的適配器,此適配器用於對應用服務器提供的高級事務的支持;
  • WebSphereUowTransactionManager:位於org.springframework.transaction.jta包中,Spring提供的對WebSphere 6.0+應用服務器事務管理器的適配器,此適配器用於對應用服務器提供的高級事務的支持;
  • WebLogicJtaTransactionManager:位於org.springframework.transaction.jta包中,Spring提供的對WebLogic 8.1+應用服務器事務管理器的適配器,此適配器用於對應用服務器提供的高級事務的支持。

Spring不只提供這些事務管理器,還提供對如JMS事務管理的管理器等,Spring提供一致的事務抽象如圖9-1所示。

圖9-1 Spring事務管理器

接下來讓咱們學習一下如何在Spring配置文件中定義事務管理器:

1、聲明對本地事務的支持:

a)JDBC及iBATIS、MyBatis框架事務管理器

java代碼:

<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

經過dataSource屬性指定須要事務管理的單個javax.sql.DataSource對象。

b)Jdo事務管理器

java代碼:

<bean id="txManager" class="org.springframework.orm.jdo.JdoTransactionManager">
    <property name="persistenceManagerFactory" ref="persistenceManagerFactory"/>
</bean>

經過persistenceManagerFactory屬性指定須要事務管理的javax.jdo.PersistenceManagerFactory對象。

c)Jpa事務管理器

java代碼:

bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>

經過entityManagerFactory屬性指定須要事務管理的javax.persistence.EntityManagerFactory對象。

還須要爲entityManagerFactory對象指定jpaDialect屬性,該屬性所對應的對象指定了如何獲取鏈接對象、開啓事務、關閉事務等事務管理相關的行爲。

java代碼:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
        ……
        <property name="jpaDialect" ref="jpaDialect"/>
</bean>
<bean id="jpaDialect" class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"/>

d)Hibernate事務管理器

java代碼:

<bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory"/>
</bean>

經過entityManagerFactory屬性指定須要事務管理的org.hibernate.SessionFactory對象。

聲明式事務

聲明式事務概述

從上節編程式實現事務管理能夠深入體會到編程式事務的痛苦,即便經過代理配置方式也是不小的工做量。

本節將介紹聲明式事務支持,使用該方式後最大的獲益是簡單,事務管理再也不是使人痛苦的,並且此方式屬於無侵入式,對業務邏輯實現無影響。

接下來先來看看聲明式事務如何實現吧。

聲明式實現事務管理

一、定義業務邏輯實現,此處使用ConfigUserServiceImpl和ConfigAddressServiceImpl:

二、定義配置文件(chapter9/service/ applicationContext-service-declare.xml):

2.一、XML命名空間定義,定義用於事務支持的tx命名空間和AOP支持的aop命名空間:

java代碼:
<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"
      xmlns:aop="http://www.springframework.org/schema/aop"
      xsi:schemaLocation="
 
http://www.springframework.org/schema/beans
 
 
http://www.springframework.org/schema/beans/spring-beans-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/aop
 
 
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

2.二、業務實現配置,很是簡單,使用之前定義的非侵入式業務實現:

java代碼:
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">
    <property name="userDao" ref="userDao"/>
    <property name="addressService" ref="addressService"/>
</bean>
<bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">
    <property name="addressDao" ref="addressDao"/>
</bean>

2.三、事務相關配置:

java代碼:
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/>
        <tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" read-only="true"/>
    </tx:attributes>
</tx:advice>

java代碼:

<aop:config>
    <aop:pointcut id="serviceMethod" expression="execution(* cn..chapter9.service..*.*(..))"/>
    <aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/>
</aop:config>
<tx:advice>:事務通知定義,用於指定事務屬性,其中「transaction-manager」屬性指定事務管理器,並經過< tx:attributes >指定具體須要攔截的方法;
 <tx:method name=」save*」>:表示將攔截以save開頭的方法,被攔截的方法將應用配置的事務屬性:propagation=」REQUIRED」表示傳播行爲是Required,isolation=」READ_COMMITTED」表示隔離級別是提交讀;
<tx:method name=」*」>:表示將攔截其餘全部方法,被攔截的方法將應用配置的事務屬性:propagation=」REQUIRED」表示傳播行爲是Required,isolation=」READ_COMMITTED」表示隔離級別是提交讀,read-only=」true」表示事務只讀;
<aop:config>:AOP相關配置:
<aop:pointcut/>:切入點定義,定義名爲」serviceMethod」的aspectj切入點,切入點表達式爲」execution(* cn..chapter9.service..*.*(..))」表示攔截cn包及子包下的chapter9. service包及子包下的任何類的任何方法;
<aop:advisor>:Advisor定義,其中切入點爲serviceMethod,通知爲txAdvice。
從配置中能夠看出,將對cn包及子包下的chapter9. service包及子包下的任何類的任何方法應用「txAdvice」通知指定的事務屬性。

三、修改測試方法並測試該配置方式是否好用:

將TransactionTest 類的testServiceTransaction測試方法拷貝一份命名爲testDeclareTransaction:

並在testDeclareTransaction測試方法內將:

四、執行測試,測試正常經過,說明該方式能正常工做,當調用save方法時將匹配到事務通知中定義的「<tx:method name=」save」>」中指定的事務屬性,而調用countAll方法時將匹配到事務通知中定義的「<tx:method name=」」>」中指定的事務屬性。

聲明式事務是如何實現事務管理的呢?還記不記得TransactionProxyFactoryBean實現配置式事務管理,配置式事務管理是經過代理方式實現,而聲明式事務管理一樣是經過AOP代理方式實現。

聲明式事務經過AOP代理方式實現事務管理,利用環繞通知TransactionInterceptor實現事務的開啓及關閉,而TransactionProxyFactoryBean內部也是經過該環繞通知實現的,所以能夠認爲是<tx:tags/>幫你定義了TransactionProxyFactoryBean,從而簡化事務管理。

瞭解了實現方式後,接下來詳細學習一下配置吧:

9.4.4 <tx:advice/>配置詳解
聲明式事務管理經過配置<tx:advice/>來定義事務屬性,配置方式以下所示:

java代碼:
<tx:advice id="……" transaction-manager="……">
<tx:attributes>
        <tx:method name="……"
                           propagation=" REQUIRED"
                           isolation="READ_COMMITTED"
                           timeout="-1"
                           read-only="false"
                           no-rollback-for=""
                           rollback-for=""/>
        ……
    </tx:attributes>
</tx:advice>
<tx:advice>:id用於指定此通知的名字, transaction-manager用於指定事務管理器,默認的事務管理器名字爲「transactionManager」;
<tx:method>:用於定義事務屬性即相關聯的方法名;

name:定義與事務屬性相關聯的方法名,將對匹配的方法應用定義的事務屬性,可使用「」通配符來匹配一組或全部方法,如「save」將匹配以save開頭的方法,而「*」將匹配全部方法;

propagation:事務傳播行爲定義,默認爲「REQUIRED」,表示Required,其值能夠經過TransactionDefinition的靜態傳播行爲變量的「PROPAGATION_」後邊部分指定,如「TransactionDefinition.PROPAGATION_REQUIRED」可使用「REQUIRED」指定;

isolation:事務隔離級別定義;默認爲「DEFAULT」,其值能夠經過TransactionDefinition的靜態隔離級別變量的「ISOLATION_」後邊部分指定,如「TransactionDefinition. ISOLATION_DEFAULT」可使用「DEFAULT」指定:

timeout:事務超時時間設置,單位爲秒,默認-1,表示事務超時將依賴於底層事務系統;

read-only:事務只讀設置,默認爲false,表示不是隻讀;

rollback-for:須要觸發回滾的異常定義,以「,」分割,默認任何RuntimeException 將致使事務回滾,而任何Checked Exception 將不致使事務回滾;異常名字定義和TransactionProxyFactoryBean中含義同樣

no-rollback-for:不被觸發進行回滾的 Exception(s);以「,」分割;異常名字定義和TransactionProxyFactoryBean中含義同樣;

記不記得在配置方式中爲了解決「自我調用」而致使的不能設置正確的事務屬性問題,使用「((IUserService)AopContext.currentProxy()).otherTransactionMethod()」方式解決,在聲明式事務要獲得支持須要使用<aop:config expose-proxy=」true」>來開啓。

9.4.5 多事務語義配置及最佳實踐
什麼是多事務語義?說白了就是爲不一樣的Bean配置不一樣的事務屬性,由於咱們項目中不可能就幾個Bean,而可能不少,這可能須要爲Bean分組,爲不一樣組的Bean配置不一樣的事務語義。在Spring中,能夠經過配置多切入點和多事務通知並經過不一樣方式組合使用便可。

一、首先看下聲明式事務配置的最佳實踐吧:

<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
           <tx:method name="save*" propagation="REQUIRED" />
           <tx:method name="add*" propagation="REQUIRED" />
           <tx:method name="create*" propagation="REQUIRED" />
           <tx:method name="insert*" propagation="REQUIRED" />
           <tx:method name="update*" propagation="REQUIRED" />
           <tx:method name="merge*" propagation="REQUIRED" />
           <tx:method name="del*" propagation="REQUIRED" />
           <tx:method name="remove*" propagation="REQUIRED" />
           <tx:method name="put*" propagation="REQUIRED" />
           <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
           <tx:method name="count*" propagation="SUPPORTS" read-only="true" />
          <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
          <tx:method name="list*" propagation="SUPPORTS" read-only="true" />
          <tx:method name="*" propagation="SUPPORTS" read-only="true" />
       </tx:attributes>
</tx:advice>
<aop:config>
       <aop:pointcut id="txPointcut" expression="execution(* cn.javass..service.*.*(..))" />
       <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut" />
</aop:config>

該聲明式事務配置能夠應付常見的CRUD接口定義,並實現事務管理,咱們只需修改切入點表達式來攔截咱們的業務實現從而對其應用事務屬性就能夠了,若是還有更復雜的事務屬性直接添加便可,即

若是咱們有一個batchSaveOrUpdate方法須要「REQUIRES_NEW」事務傳播行爲,則直接添加以下配置便可:

java代碼:
1
<tx:method name="batchSaveOrUpdate" propagation="REQUIRES_NEW" />
二、接下來看一下多事務語義配置吧,聲明式事務最佳實踐中已經配置了通用事務屬性,所以能夠針對須要其餘事務屬性的業務方法進行特例化配置:

java代碼:
<tx:advice id="noTxAdvice" transaction-manager="txManager">
    <tx:attributes>
           <tx:method name="*" propagation="NEVER" />
    </tx:attributes>
</tx:advice>
<aop:config>
       <aop:pointcut id="noTxPointcut" expression="execution(* cn.javass..util.*.*())" />
       <aop:advisor advice-ref="noTxPointcut" pointcut-ref="noTxAdvice" />
</aop:config>

該聲明將對切入點匹配的方法所在事務應用「Never」傳播行爲。

多事務語義配置時,切入點必定不要疊加,不然將應用兩次事務屬性,形成沒必要要的錯誤及麻煩。

@Transactional實現事務管理

對聲明式事務管理,Spring提供基於@Transactional註解方式來實現,但須要Java 5+。

註解方式是最簡單的事務配置方式,能夠直接在Java源代碼中聲明事務屬性,且對於每個業務類或方法若是須要事務都必須使用此註解。

接下來學習一下註解事務的使用吧:

一、定義業務邏輯實現:

package cn.javass.spring.chapter9.service.impl;
//省略import
public class AnnotationUserServiceImpl implements IUserService {
    private IUserDao userDao;
    private IAddressService addressService;
    public void setUserDao(IUserDao userDao) {
        this.userDao = userDao;
    }
    public void setAddressService(IAddressService addressService) {
        this.addressService = addressService;
    }
    @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED)
    @Override
    public void save(final UserModel user) {
        userDao.save(user);
        user.getAddress().setUserId(user.getId());
        addressService.save(user.getAddress());
    }
    @Transactional(propagation=Propagation.REQUIRED, isolation=Isolation.READ_COMMITTED, readOnly=true)
    @Override
    public int countAll() {
        return userDao.countAll();
    }
}

二、定義配置文件(chapter9/service/ applicationContext-service-annotation.xml):

2.一、XML命名空間定義,定義用於事務支持的tx命名空間和AOP支持的aop命名空間:

java代碼:

<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"
          xmlns:aop="http://www.springframework.org/schema/aop"
          xsi:schemaLocation="
     
    http://www.springframework.org/schema/beans
     
     
    http://www.springframework.org/schema/beans/spring-beans-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/aop
     
     
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

2.二、業務實現配置,很是簡單,使用之前定義的非侵入式業務實現:

java代碼:
<bean id="userService" class="cn.javass.spring.chapter9.service.impl.ConfigUserServiceImpl">
    <property name="userDao" ref="userDao"/>
    <property name="addressService" ref="addressService"/>
</bean>
<bean id="addressService" class="cn.javass.spring.chapter9.service.impl.ConfigAddressServiceImpl">
    <property name="addressDao" ref="addressDao"/>
</bean>

2.三、事務相關配置:

java代碼:
1
<tx:annotation-driven transaction-manager="txManager"/>
使用如上配置已支持聲明式事務。

三、修改測試方法並測試該配置方式是否好用:

將TransactionTest 類的testServiceTransaction測試方法拷貝一份命名爲testAnntationTransactionTest:



classpath:chapter9/service/applicationContext-service-annotation.xml"

userService.save(user);
try {
    userService.save(user);
    Assert.fail();
} catch (RuntimeException e) {
}
Assert.assertEquals(0, userService.countAll());
Assert.assertEquals(0, addressService.countAll());

四、執行測試,測試正常經過,說明該方式能正常工做,由於在AnnotationAddressServiceImpl類的save方法中拋出異常,所以事務須要回滾,因此兩個countAll操做都返回0。

9.4.7 @Transactional配置詳解
Spring提供的<tx:annotation-driven/>用於開啓對註解事務管理的支持,從而能識別Bean類上的@Transactional註解元數據,其具備如下屬性:

transaction-manager:指定事務管理器名字,默認爲transactionManager,當使用其餘名字時須要明確指定;
proxy-target-class:表示將使用的代碼機制,默認false表示使用JDK代理,若是爲true將使用CGLIB代理
order:定義事務通知順序,默認Ordered.LOWEST_PRECEDENCE,表示將順序決定權交給AOP來處理。
Spring使用@Transactional 來指定事務屬性,能夠在接口、類或方法上指定,若是類和方法上都指定了@Transactional ,則方法上的事務屬性被優先使用,具體屬性以下:

value:指定事務管理器名字,默認使用<tx:annotation-driven/>指定的事務管理器,用於支持多事務管理器環境;
propagation:指定事務傳播行爲,默認爲Required,使用Propagation.REQUIRED指定;
isolation:指定事務隔離級別,默認爲「DEFAULT」,使用Isolation.DEFAULT指定;
readOnly:指定事務是否只讀,默認false表示事務非只讀;
timeout:指定事務超時時間,以秒爲單位,默認-1表示事務超時將依賴於底層事務系統;
rollbackFor:指定一組異常類,遇到該類異常將回滾事務;
rollbackForClassname:指定一組異常類名字,其含義與<tx:method>中的rollback-for屬性語義徹底同樣;
noRollbackFor:指定一組異常類,即便遇到該類異常也將提交事務,即不回滾事務;
noRollbackForClassname:指定一組異常類名字,其含義與<tx:method>中的no-rollback-for屬性語義徹底同樣;
Spring提供的@Transactional 註解事務管理內部一樣利用環繞通知TransactionInterceptor實現事務的開啓及關閉。

使用@Transactional註解事務管理須要特別注意如下幾點:

若是在接口、實現類或方法上都指定了@Transactional 註解,則優先級順序爲方法>實現類>接口;
建議只在實現類或實現類的方法上使用@Transactional,而不要在接口上使用,這是由於若是使用JDK代理機制是沒問題,由於其使用基於接口的代理;而使用使用CGLIB代理機制時就會遇到問題,由於其使用基於類的代理而不是接口,這是由於接口上的@Transactional註解是「不能繼承的」;

具體請參考基於JDK動態代理和CGLIB動態代理的實現Spring註解管理事務(@Trasactional)到底有什麼區別。

在Spring代理機制下(不論是JDK動態代理仍是CGLIB代理),「自我調用」一樣不會應用相應的事務屬性,其語義和<tx:tags>中同樣;
默認只對RuntimeException異常回滾;
在使用Spring代理時,默認只有在public可見度的方法的@Transactional 註解纔是有效的,其它可見度(protected、private、包可見)的方法上即便有@Transactional 註解也不會應用這些事務屬性的,Spring也不會報錯,若是你非要使用非公共方法註解事務管理的話,可考慮使用AspectJ。

微信公衆號【黃小斜】做者是螞蟻金服 JAVA 工程師,專一於 JAVA 後端技術棧:SpringBoot、SSM全家桶、MySQL、分佈式、中間件、微服務,同時也懂點投資理財,堅持學習和寫做,相信終身學習的力量!關注公衆號後回覆」架構師「便可領取 Java基礎、進階、項目和架構師等免費學習資料,更有數據庫、分佈式、微服務等熱門技術學習視頻,內容豐富,兼顧原理和實踐,另外也將贈送做者原創的Java學習指南、Java程序員面試指南等乾貨資源
相關文章
相關標籤/搜索