Spring-AOP事物管理

1.概述

Spring 中的事務主要是利用 Aop 思想,簡化事務的配置

2.核心接口

Spring事務管理的實現(xiàn)有許多細節(jié)癣漆,如果對整個接口框架有個大體了解會非常有利于我們理解事務暑脆。
下面通過講解Spring的事務接口來了解Spring實現(xiàn)事務管理的具體策略渠啤。
Spring事務管理涉及的接口的聯(lián)系如下:

2.1事物管理器

Spring并不直接管理事務,而是提供了多種事務管理器饵筑,他們將事務管理的職責委托給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現(xiàn)埃篓。

Spring事務管理器的接口是org.springframework.transaction.PlatformTransactionManager,通過這個接口根资,Spring為各個平臺如JDBC架专、Hibernate等都提供了對應的事務管理器,但是具體的實現(xiàn)就是各個平臺自己的事情了玄帕。
此接口的內(nèi)容如下:

package org.springframework.transaction;

import org.springframework.lang.Nullable;

/**
 * This is the central interface in Spring's transaction infrastructure.
 * Applications can use this directly, but it is not primarily meant as API:
 * Typically, applications will work with either TransactionTemplate or
 * declarative transaction demarcation through AOP.
 *
 * <p>For implementors, it is recommended to derive from the provided
 * {@link org.springframework.transaction.support.AbstractPlatformTransactionManager}
 * class, which pre-implements the defined propagation behavior and takes care
 * of transaction synchronization handling. Subclasses have to implement
 * template methods for specific states of the underlying transaction,
 * for example: begin, suspend, resume, commit.
 *
 * <p>The default implementations of this strategy interface are
 * {@link org.springframework.transaction.jta.JtaTransactionManager} and
 * {@link org.springframework.jdbc.datasource.DataSourceTransactionManager},
 * which can serve as an implementation guide for other transaction strategies.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @since 16.05.2003
 * @see org.springframework.transaction.support.TransactionTemplate
 * @see org.springframework.transaction.interceptor.TransactionInterceptor
 */
public interface PlatformTransactionManager extends TransactionManager {  
    // 由TransactionDefinition得到TransactionStatus對象
    TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException; 
    // 提交
    Void commit(TransactionStatus status) throws TransactionException;  
    // 回滾
    Void rollback(TransactionStatus status) throws TransactionException;  
} 

從這里可知部脚,具體的事務管理機制對Spring來說是透明的,它并不關心那些裤纹,那些是對應各個平臺需要關心的委刘。
所以Spring事務管理的一個優(yōu)點就是為不同的事務API提供一致的編程模型,如JDBC鹰椒、Hibernate锡移、JPA、JTA漆际。
下面分別介紹各個平臺框架實現(xiàn)事務管理的機制淆珊。

  • JDBC事物
    如果應用程序中直接使用JDBC來進行持久化,DataSourceTransactionManager會為你處理事務邊界奸汇。
    為了使用DataSourceTransactionManager施符,需要使用如下的XML將其裝配到應用程序的上下文定義中:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

實際上,DataSourceTransactionManager是通過調(diào)用java.sql.Connection來管理事務擂找,而后者是通過DataSource獲取到的戳吝。
通過調(diào)用連接的commit()方法來提交事務,同樣贯涎,事務失敗則通過調(diào)用rollback()方法進行回滾听哭。

  • Hibernate事務
    如果應用程序的持久化是通過Hibernate實現(xiàn)的,那么需要使用HibernateTransactionManager。
    對于Hibernate3欢唾,需要在Spring上下文定義中添加如下的<bean>聲明:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

sessionFactory屬性需要裝配一個Hibernate的session工廠且警。
HibernateTransactionManager的實現(xiàn)細節(jié)是它將事務管理的職責委托給org.hibernate.Transaction對象粉捻,而后者是從Hibernate Session中獲取到的礁遣。
當事務成功完成時,HibernateTransactionManager將會調(diào)用Transaction對象的commit()方法肩刃,反之祟霍,將會調(diào)用rollback()方法

  • Java持久化API事務(JPA)
    Hibernate多年來一直是事實上的Java持久化標準,但是現(xiàn)在Java持久化API作為真正的Java持久化標準進入大家的視野盈包。
    如果你計劃使用JPA的話沸呐,那你需要使用Spring的JpaTransactionManager來處理事務。
    你需要在Spring中這樣配置JpaTransactionManager:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

JpaTransactionManager只需要裝配一個JPA實體管理工廠(javax.persistence.EntityManagerFactory接口的任意實現(xiàn))呢燥。
JpaTransactionManager將與由工廠所產(chǎn)生的JPA EntityManager合作來構(gòu)建事務崭添。

  • Java原生API事務
    如果你沒有使用以上所述的事務管理,或者是跨越了多個事務管理源(比如兩個或者是多個不同的數(shù)據(jù)源)叛氨,你就需要使用JtaTransactionManager:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
    <property name="transactionManagerName" value="java:/TransactionManager" />
</bean>

JtaTransactionManager將事務管理的責任委托給javax.transaction.UserTransaction和javax.transaction.TransactionManager對象呼渣。
其中事務成功完成通過UserTransaction.commit()方法提交,事務失敗通過UserTransaction.rollback()方法回滾寞埠。

2.2 基本事務屬性的定義

上面講到的事務管理器接口PlatformTransactionManager通過getTransaction(TransactionDefinition definition)方法來得到事務屁置,這個方法里面的參數(shù)是TransactionDefinition類,這個類就定義了一些基本的事務屬性仁连。
那么什么是事務屬性呢蓝角?事務屬性可以理解成事務的一些基本配置,描述了事務策略如何應用到方法上饭冬。
事務屬性包含了5個方面使鹅,如圖所示:

2.2.1事物傳播行為

事務的傳播行為(propagation behavior):當事務方法被另一個事務方法調(diào)用時,必須指定事務應該如何傳播昌抠。
例如:方法可能繼續(xù)在現(xiàn)有事務中運行患朱,也可能開啟一個新事務,并在自己的事務中運行扰魂。
Spring定義了七種傳播行為:

傳播行為 含義
PROPAGATION_REQUIRED 表示當前方法必須運行在事務中麦乞。
如果當前事務存在,方法將會在該事務中運行劝评。否則姐直,會啟動一個新的事務
PROPAGATION_SUPPORTS 表示當前方法不需要運行在事務中。
但是如果存在當前事務的話蒋畜,那么該方法會在這個事務中運行
PROPAGATION_MANDATORY 表示該方法必須在事務中運行声畏,如果當前事務不存在,則會拋出一個異常
PROPAGATION_REQUIRED_NEW 表示當前方法必須運行在它自己的事務中,一個新的事務將被啟動插龄。
如果存在當前事務愿棋,在該方法執(zhí)行期間,當前事務會被掛起均牢。
如果使用JTATransactionManager的話糠雨,則需要訪問TransactionManager
PROPAGATION_NOT_SUPPORTED 表示該方法不應該運行在事務中。
如果存在當前事務徘跪,在該方法運行期間甘邀,當前事務將被掛起。
如果使用JTATransactionManager的話垮庐,則需要訪問TransactionManager
PROPAGATION_NEVER 表示當前方法不應該運行在事務上下文中松邪。
如果當前正有一個事務在運行,則會拋出異常
PROPAGATION_NESTED 表示如果當前已經(jīng)存在一個事務哨查,那么該方法將會在嵌套事務中運行逗抑。
嵌套的事務可以獨立于當前事務進行單獨地提交或回滾。如果當前事務不存在寒亥,那么其行為與PROPAGATION_REQUIRED一樣邮府。
注意各廠商對這種傳播行為的支持是有所差異的』び可以參考資源管理器的文檔來確認它們是否支持嵌套事務
具體實例:
  • PROPAGATION_REQUIRED
    如果存在一個事務挟纱,則支持當前事務。
    如果沒有事務則開啟一個新的事務腐宋。
//事務屬性 PROPAGATION_REQUIRED
methodA{
    ……
    methodB();
    ……
}

使用spring聲明式事務紊服,spring使用AOP來支持聲明式事務,會根據(jù)事務屬性胸竞,自動在方法調(diào)用之前決定是否開啟一個事務欺嗤,并在方法執(zhí)行之后決定事務提交或回滾事務。
單獨調(diào)用methodB方法:

main{ 
    metodB(); 
} 

相當于:

Main{ 
    Connection con=null; 
    try{ 
        con = getConnection(); 
        con.setAutoCommit(false); 
        //方法調(diào)用
        methodB(); 
        //提交事務
        con.commit(); 
    } Catch(RuntimeException ex) { 
        //回滾事務
        con.rollback();   
    } finally { 
        //釋放資源
        closeCon(); 
    } 
} 

Spring保證在methodB方法中的調(diào)用都獲得到一個相同的連接卫枝。
在單獨調(diào)用methodB時煎饼,沒有一個存在的事務,所以獲得一個新的連接校赤,開啟了一個新的事務吆玖。

單獨調(diào)用MethodA時,在MethodA內(nèi)又會調(diào)用MethodB马篮,執(zhí)行效果相當于:

main{ 
    Connection con = null; 
    try{ 
        con = getConnection(); 
        methodA(); 
        con.commit(); 
    } catch(RuntimeException ex) { 
        con.rollback(); 
    } finally {    
        closeCon(); 
    }  
} 

調(diào)用MethodA時沾乘,環(huán)境中沒有事務,所以開啟一個新的事務
當在MethodA中調(diào)用MethodB時浑测,環(huán)境中已經(jīng)有了一個事務翅阵,所以methodB就加入當前事務歪玲。

  • PROPAGATION_SUPPORTS
    如果存在一個事務,支持當前事務掷匠。
    如果沒有事務滥崩,則以非事務的方式執(zhí)行。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
  methodB();
}
//事務屬性 PROPAGATION_SUPPORTS
methodB(){
  ……
}

單純的調(diào)用methodB時讹语,methodB方法是非事務的執(zhí)行的钙皮。
當調(diào)用methdA時,methodB則加入了methodA的事務中,事務地執(zhí)行。

  • PROPAGATION_MANDATORY
    如果已經(jīng)存在一個事務募强,支持當前事務株灸。
    如果沒有一個活動的事務,則拋出異常擎值。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
    methodB();
}

//事務屬性 PROPAGATION_MANDATORY
methodB(){
    ……
}

當單獨調(diào)用methodB時,因為當前沒有一個活動的事務逐抑,則會拋出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”)鸠儿。
當調(diào)用methodA時,methodB則加入到methodA的事務中厕氨,事務地執(zhí)行进每。

  • PROPAGATION_REQUIRES_NEW
    總是開啟一個新的事務。
    如果一個事務已經(jīng)存在命斧,則將這個存在的事務掛起田晚。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

//事務屬性 PROPAGATION_REQUIRES_NEW
methodB(){
    ……
}

調(diào)用A方法:

main(){
    methodA();
}

相當于:

main(){
    TransactionManager tm = null;
    try{
        //獲得一個JTA事務管理器
        tm = getTransactionManager();
        tm.begin();//開啟一個新的事務
        Transaction ts1 = tm.getTransaction();
        doSomeThing();
        tm.suspend();//掛起當前事務
        try{
            tm.begin();//重新開啟第二個事務
            Transaction ts2 = tm.getTransaction();
            methodB();
            ts2.commit();//提交第二個事務
        } Catch(RunTimeException ex) {
            ts2.rollback();//回滾第二個事務
        } finally {
            //釋放資源
        }
        //methodB執(zhí)行完后,恢復第一個事務
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//提交第一個事務
    } catch(RunTimeException ex) {
        ts1.rollback();//回滾第一個事務
    } finally {
        //釋放資源
    }
}

在這里国葬,我把ts1稱為外層事務贤徒,ts2稱為內(nèi)層事務。
從上面的代碼可以看出汇四,ts2與ts1是兩個獨立的事務接奈,互不相干,Ts2是否成功并不依賴于ts1通孽。
如果methodA方法在調(diào)用methodB方法后的doSomeThingB方法失敗了序宦,而methodB方法所做的結(jié)果依然被提交,而除了methodB之外的其它代碼導致的結(jié)果卻被回滾了背苦。
使用PROPAGATION_REQUIRES_NEW需要使用JtaTransactionManager作為事務管理器互捌。

  • PROPAGATION_NOT_SUPPORTED
    總是非事務地執(zhí)行,并掛起任何存在的事務行剂。
    代碼示例同上秕噪,可同理推出
    使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。
  • PROPAGATION_NEVER
    總是非事務地執(zhí)行硼讽,如果存在一個活動事務巢价,則拋出異常。
  • PROPAGATION_NESTED
    如果一個活動的事務存在,則運行在一個嵌套的事務中壤躲。
    如果沒有活動事務城菊,則按TransactionDefinition.PROPAGATION_REQUIRED屬性執(zhí)行。
    這是一個嵌套事務碉克,使用JDBC 3.0驅(qū)動時凌唬,僅僅支持DataSourceTransactionManager作為事務管理器,需要JDBC驅(qū)動的java.sql.Savepoint類漏麦。
    有一些JTA的事務管理器實現(xiàn)可能也提供了同樣的功能客税。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true撕贞、而nestedTransactionAllowed屬性值默認為false更耻。
//事務屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

//事務屬性 PROPAGATION_NESTED
methodB(){
    ……
}

如果單獨調(diào)用methodB方法,則按REQUIRED屬性執(zhí)行捏膨。
如果調(diào)用methodA方法秧均,相當于下面的效果:

main(){
  Connection con = null;
  Savepoint savepoint = null;
  try{
    con = getConnection();
    con.setAutoCommit(false);
    doSomeThingA();
    savepoint = con2.setSavepoint();
    try{
       methodB();
    }catch(RuntimeException ex){
       con.rollback(savepoint);
    }finally{
     //釋放資源
    }

    doSomeThingB();
    con.commit();
  }catch(RuntimeException ex){
    con.rollback();
  }finally{
   //釋放資源
  }
}

當methodB方法調(diào)用之前,調(diào)用setSavepoint方法号涯,保存當前的狀態(tài)到savepoint目胡。
如果methodB方法調(diào)用失敗,則恢復到之前保存的狀態(tài)链快。
但是需要注意的是誉己,這時的事務并沒有進行提交,如果后續(xù)的代碼(doSomeThingB()方法)調(diào)用失敗域蜗,則回滾包括methodB方法的所有操作巨双。

嵌套事務一個非常重要的概念,就是內(nèi)層事務依賴于外層事務地消。
外層事務失敗時炉峰,會回滾內(nèi)層事務所做的動作。
而內(nèi)層事務操作失敗并不會引起外層事務的回滾脉执。

PROPAGATION_NESTED與PROPAGATION_REQUIRES_NEW的區(qū)別:它們非常類似疼阔,都像一個嵌套事務,如果不存在一個活動的事務半夷,都會開啟一個新的事務婆廊。使用 PROPAGATION_REQUIRES_NEW時,內(nèi)層事務與外層事務就像兩個獨立的事務一樣巫橄,一旦內(nèi)層事務進行了提交后淘邻,外層事務不能對其進行回滾。兩個事務互不影響湘换。兩個事務不是一個真正的嵌套事務宾舅。同時它需要JTA事務管理器的支持统阿。

使用PROPAGATION_NESTED時,外層事務的回滾可以引起內(nèi)層事務的回滾筹我。而內(nèi)層事務的異常并不會導致外層事務的回滾扶平,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED時蔬蕊,需要JDBC 3.0以上驅(qū)動及1.4以上的JDK版本支持结澄。其它的JTA TrasactionManager實現(xiàn)可能有不同的支持方式。

PROPAGATION_REQUIRES_NEW 啟動一個新的岸夯,不依賴于環(huán)境的 “內(nèi)部” 事務麻献。這個事務將被完全 commited 或 rolled back而不依賴于外部事務,它擁有自己的隔離范圍猜扮,自己的鎖等等勉吻。當內(nèi)部事務開始執(zhí)行時,外部事務將被掛起破镰,內(nèi)務事務結(jié)束時餐曼,外部事務將繼續(xù)執(zhí)行。

另一方面, PROPAGATION_NESTED 開始一個“嵌套的”事務鲜漩,它是已經(jīng)存在事務的一個真正的子事務。
嵌套事務開始執(zhí)行時, 它將取得一個savepoint集惋,如果這個嵌套事務失敗孕似,我們將回滾到此 savepoint。
潛套事務是外部事務的一部分, 只有外部事務結(jié)束后它才會被提交刮刑。

由此可見PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區(qū)別在于PROPAGATION_REQUIRES_NEW 完全是一個新的事務喉祭,而 PROPAGATION_NESTED 則是外部事務的子事務,如果外部事務 commit雷绢,嵌套事務也會被 commit泛烙,這個規(guī)則同樣適用于 roll back。

PROPAGATION_REQUIRED應該是我們首先的事務傳播行為翘紊。它能夠滿足我們大多數(shù)的事務需求蔽氨。

2.2.2隔離級別

事務的第二個維度就是隔離級別(isolation level)
隔離級別定義了一個事務可能受其他并發(fā)事務影響的程度。
不考慮事物隔離級別會導致的問題:
在典型的應用程序中帆疟,多個事務并發(fā)運行鹉究,經(jīng)常會操作相同的數(shù)據(jù)來完成各自的任務。并發(fā)雖然是必須的踪宠,但可能會導致一下的問題

  • 臟讀(Dirty reads)
    臟讀發(fā)生在一個事務讀取了另一個事務改寫但尚未提交的數(shù)據(jù)時自赔。
    如果改寫在稍后被回滾了,那么第一個事務獲取的數(shù)據(jù)就是無效的柳琢。
  • 不可重復讀(Nonrepeatable read)
    不可重復讀發(fā)生在一個事務執(zhí)行相同的查詢兩次或兩次以上绍妨,但是每次都得到不同的數(shù)據(jù)時润脸。
    這通常是因為另一個并發(fā)事務在兩次查詢期間進行了更新。
  • 幻讀(Phantom read)
    幻讀與不可重復讀類似他去。它發(fā)生在一個事務(T1)讀取了幾行數(shù)據(jù)毙驯,接著另一個并發(fā)事務(T2)插入了一些數(shù)據(jù)時。
    在隨后的查詢中孤页,第一個事務(T1)就會發(fā)現(xiàn)多了一些原本不存在的記錄尔苦。

以上在以前的文章中我們已經(jīng)說過,不再贅述行施。

Spring中事物的隔離級別設置
隔離級別 含義
ISOLATION_DEFAULT 使用后端數(shù)據(jù)庫默認的隔離級別
ISOLATION_READ_UNCOMMITTED 最低的隔離級別允坚,允許讀取尚未提交的數(shù)據(jù)變更,可能會導致臟讀蛾号、幻讀或不可重復讀
ISOLATION_READ_COMMITTED 允許讀取并發(fā)事務已經(jīng)提交的數(shù)據(jù)稠项,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發(fā)生
ISOLATION_REPEATABLE_READ 對同一字段的多次讀取結(jié)果都是一致的鲜结,除非數(shù)據(jù)是被本身事務自己所修改展运,可以阻止臟讀和不可重復讀,但幻讀仍有可能發(fā)生
ISOLATION_SERIALIZABLE 最高的隔離級別精刷,完全服從ACID的隔離級別拗胜,確保阻止臟讀、不可重復讀以及幻讀怒允,也是最慢的事務隔離級別埂软,因為它通常是通過完全鎖定事務相關的數(shù)據(jù)庫表來實現(xiàn)的
2.2.3只讀

Spring中事務的第三個屬性是它是否為只讀事務。
如果事務只對后端的數(shù)據(jù)庫進行該操作纫事,數(shù)據(jù)庫可以利用事務的只讀特性來進行一些特定的優(yōu)化勘畔。
通過將事務設置為只讀,你就可以給數(shù)據(jù)庫一個機會丽惶,讓它應用它認為合適的優(yōu)化措施炫七。
這里需要注意:Spring中事物readOnly的定義,并不是不能在事務中進行修改等DML操作钾唬,它只是一個“暗示”万哪,提示數(shù)據(jù)庫驅(qū)動程序和數(shù)據(jù)庫系統(tǒng),這個事務并不包含更改數(shù)據(jù)的操作知纷,那么JDBC驅(qū)動程序和數(shù)據(jù)庫就有可能根據(jù)這種情況對該事務進行一些特定的優(yōu)化壤圃,比方說不安排相應的數(shù)據(jù)庫鎖,以減輕事務對數(shù)據(jù)庫的壓力琅轧,畢竟事務也是要消耗數(shù)據(jù)庫的資源的伍绳。
只讀事務僅僅是一個性能優(yōu)化的推薦配置而已,并非強制你非要這樣處理不可乍桂。
結(jié)合源碼的一個解讀分析具體是通過什么方式來進行優(yōu)化:
如果是只讀事務冲杀,Spring會將transactionContext的isFlushModeNever 設置為true效床;
當事務被標識為只讀事務時,Spring可以對某些可以針對只讀事務進行優(yōu)化的資源就可以執(zhí)行相應的優(yōu)化措施权谁,上面Spring告之hibernate的session在只讀事務模式下不用嘗試檢測和同步持久對象的狀態(tài)的更新剩檀。
總結(jié):
如果在使用事務的情況下,所有操作都是讀操作旺芽,那建議把事務設置成只讀事務沪猴,或者事務的傳播途徑最好能設置為 supports (運行在當前的事務范圍內(nèi),如果當前沒有啟動事務采章,那么就不在事務范圍內(nèi)運行)或者 not supports (不在事務范圍內(nèi)執(zhí)行运嗜,如果當前啟動了事務,那么掛起當前事務)悯舟,這樣不在事務下担租,就不會調(diào)用transactionContext.managedFlush(); 方法。
所有只讀事務與讀寫事務的比較大的運行性能區(qū)別就是只讀事務避免了Hibernate的檢測和同步持久對象的狀態(tài)的更新,提升了運行性能抵怎。

2.2.4事務超時

為了使應用程序很好地運行奋救,事務不能運行太長的時間。
因為事務可能涉及對后端數(shù)據(jù)庫的鎖定反惕,所以長時間的事務會不必要的占用數(shù)據(jù)庫資源尝艘。
事務超時就是事務的一個定時器,在特定時間內(nèi)事務如果沒有執(zhí)行完畢姿染,那么就會自動回滾利耍,而不是一直等待其結(jié)束。
默認設置為底層事務系統(tǒng)的超時值盔粹,如果底層數(shù)據(jù)庫事務系統(tǒng)沒有設置超時值,那么就是none程癌,沒有超時限制舷嗡。
推薦閱讀文章:http://www.heartthinkdo.com/?p=910

2.2.5 回滾規(guī)則

事務五邊形的最后一個方面是一組規(guī)則,這些規(guī)則定義了哪些異常會導致事務回滾而哪些不會嵌莉。
默認情況下进萄,事務只有遇到運行期異常時才會回滾,而在遇到檢查型異常時不會回滾(這一行為與EJB的回滾行為是一致的)
但是你可以聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾锐峭。
同樣中鼠,你還可以聲明事務遇到特定的異常不回滾,即使這些異常是運行期異常沿癞。
2.3 事務狀態(tài)
上面講到的調(diào)用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現(xiàn)援雇,這個接口的內(nèi)容如下:

public interface TransactionStatus{
    boolean isNewTransaction(); // 是否是新的事物
    boolean hasSavepoint(); // 是否有恢復點
    void setRollbackOnly();  // 設置為只回滾
    boolean isRollbackOnly(); // 是否為只回滾
    boolean isCompleted; // 是否已完成
} 

可以發(fā)現(xiàn)這個接口描述的是一些處理事務提供簡單的控制事務執(zhí)行和查詢事務狀態(tài)的方法,在回滾或提交的時候需要應用對應的事務狀態(tài)椎扬。

3.編程式事物

Spring提供了對編程式事務和聲明式事務的支持惫搏,編程式事務允許用戶在代碼中精確定義事務的邊界具温,而聲明式事務(基于AOP)有助于用戶將操作與事務規(guī)則進行解耦。
簡單地說筐赔,編程式事務侵入到了業(yè)務代碼里面铣猩,但是提供了更加詳細的事務管理;而聲明式事務由于基于AOP茴丰,所以既能起到事務管理的作用达皿,又可以不影響業(yè)務代碼的具體實現(xiàn)。

Spring提供兩種方式的編程式事務管理贿肩,分別是:使用TransactionTemplate和直接使用PlatformTransactionManager峦椰。

因為日常開發(fā)中極少使用編程式事務,不再做介紹尸曼,想要了解的同學自行百度

4.聲明式事物

聲明式事務基于SpringAOP们何,它是SpringAOP的典型應用場景,Spring中聲明式事物的配置方式常用的有兩種:

  • 基于XML的聲明式事物配置
  • 基于注解的聲明式事物配置

4.1基于XML的聲明式事物配置

XML中配置事務一共分為三個步驟:

  • 1.配置TransactionManager
<!-- 非Tomcat應用服務器環(huán)境下推薦使用JTA事務管理器控轿,Tomcat應用服務器環(huán)境下推薦使用純粹的JDBC事務管理器冤竹。 -->
<!--JTA事務管理器,使用該選項時候茬射,必須使用一個容器管理的DataSource(即使用應用服務器JNDI數(shù)據(jù)源) -->
<!--
<bean id="txManager"
    class="org.springframework.transaction.jta.JtaTransactionManager" />
 -->
<!--純粹的JDBC事務管理器, 框架使用JTA事務鹦蠕,該選項一般不打開,缺省下打開是考慮支持所有的應用服務器 -->
<bean id="txManager"
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--純粹的Hibernate的本地事務在抛,框架使用JTA事務 -->
<!--
    <bean id="txManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" /> </bean>
-->
  • 2.配置事務要處理的方法
<!-- spring聲明式事務的配置钟病,以下為spring的AOP事務管理的增強部分 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- 需要由交給spring的aop來進行代理的方法的集合,如果應用有自己的方法需有由spring來進行事務控制必須添加方法-->
        <!--
            PROPAGATION_REQUIRED:支持當前事務刚梭,如果當前沒有事務肠阱,就新建一個事務。這是最常見的選擇朴读。
            PROPAGATION_SUPPORTS:支持當前事務屹徘,如果當前沒有事務,就以非事務方式執(zhí)行衅金。
            PROPAGATION_MANDATORY:支持當前事務噪伊,如果當前沒有事務,就拋出異常氮唯。
            PROPAGATION_REQUIRES_NEW:新建事務鉴吹,如果當前存在事務,把當前事務掛起惩琉。
            PROPAGATION_NOT_SUPPORTED:以非事務方式執(zhí)行操作豆励,如果當前存在事務,就把當前事務掛起琳水。
            PROPAGATION_NEVER:以非事務方式執(zhí)行肆糕,如果當前存在事務般堆,則拋出異常。
            PROPAGATION_NESTED:支持當前事務诚啃,如果當前事務存在淮摔,則執(zhí)行一個嵌套事務,如果當前沒有事務始赎,就新建一個事務和橙。
         -->
        <!-- 讀取數(shù)據(jù)方法,一般采用只讀事務-->
        <tx:method name="get*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true"/>
        <tx:method name="load*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true"/>
        <tx:method name="select*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true"/>
        <tx:method name="list*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true"/>
        <tx:method name="query*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true"/>
        <tx:method name="criteria*" isolation="DEFAULT"
                   propagation="SUPPORTS" read-only="true"/>


        <!--其他方法造垛,如save魔招,update,insert等對數(shù)據(jù)庫進行寫入操作的方法五辽,當產(chǎn)生ServiceException進行回滾 -->
        <tx:method name="init*" isolation="DEFAULT"
                   read-only="false" propagation="REQUIRED"
                   rollback-for="ServiceException"/>
        <tx:method name="insert*" isolation="DEFAULT"
                   read-only="false" propagation="REQUIRED"
                   rollback-for="ServiceException"/>
        <tx:method name="update*" isolation="DEFAULT"
                   read-only="false" propagation="REQUIRED"
                   rollback-for="ServiceException"/>
        <tx:method name="save*" isolation="DEFAULT"
                   read-only="false" propagation="REQUIRED"
                   rollback-for="ServiceException"/>
        <tx:method name="add*" isolation="DEFAULT" read-only="false"
                   propagation="REQUIRED" rollback-for="ServiceException"/>
        <tx:method name="create*" isolation="DEFAULT"
                   read-only="false" propagation="REQUIRED"
                   rollback-for="ServiceException"/>
        <tx:method name="del*" isolation="DEFAULT" read-only="false"
                   propagation="REQUIRED" rollback-for="ServiceException"/>
        <tx:method name="remove*" isolation="DEFAULT"
                   read-only="false" propagation="REQUIRED"
                   rollback-for="ServiceException"/>
        <tx:method name="process" isolation="DEFAULT" read-only="false"
                   propagation="REQUIRED" rollback-for="ServiceException"/>
    </tx:attributes>
</tx:advice>

注意:一旦配置了方法名稱規(guī)則之后办斑,service 中的方法一定要按照這里的名稱規(guī)則來,否則事務配置不會生效

  • 3.配置 Aop
<aop:config>
    <aop:pointcut id="serviceOperation"
                  expression="execution(* com.tp..*Service.*(..))"/>
    <aop:advisor pointcut-ref="serviceOperation"
                 advice-ref="txAdvice"/>
</aop:config>

完整的配置信息:

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

    <!-- 非Tomcat應用服務器環(huán)境下推薦使用JTA事務管理器杆逗,Tomcat應用服務器環(huán)境下推薦使用純粹的JDBC事務管理器乡翅。 -->
    <!--JTA事務管理器,使用該選項時候罪郊,必須使用一個容器管理的DataSource(即使用應用服務器JNDI數(shù)據(jù)源) -->
    <!--
    <bean id="txManager"
        class="org.springframework.transaction.jta.JtaTransactionManager" />
     -->
    <!--純粹的JDBC事務管理器, 框架使用JTA事務蠕蚜,該選項一般不打開,缺省下打開是考慮支持所有的應用服務器 -->
    <bean id="txManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--純粹的Hibernate的本地事務悔橄,框架使用JTA事務 -->
    <!--
        <bean id="txManager"
        class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" /> </bean>
    -->

    <!-- spring聲明式事務的配置靶累,以下為spring的AOP事務管理的增強部分 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!-- 需要由交給spring的aop來進行代理的方法的集合,如果應用有自己的方法需有由spring來進行事務控制必須添加方法-->
            <!--
                PROPAGATION_REQUIRED:支持當前事務癣疟,如果當前沒有事務挣柬,就新建一個事務。這是最常見的選擇睛挚。
                PROPAGATION_SUPPORTS:支持當前事務凛忿,如果當前沒有事務,就以非事務方式執(zhí)行竞川。
                PROPAGATION_MANDATORY:支持當前事務,如果當前沒有事務叁熔,就拋出異常委乌。
                PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務荣回,把當前事務掛起闺魏。
                PROPAGATION_NOT_SUPPORTED:以非事務方式執(zhí)行操作载佳,如果當前存在事務,就把當前事務掛起翅娶。
                PROPAGATION_NEVER:以非事務方式執(zhí)行,如果當前存在事務狭园,則拋出異常。
                PROPAGATION_NESTED:支持當前事務,如果當前事務存在踏堡,則執(zhí)行一個嵌套事務,如果當前沒有事務咒劲,就新建一個事務顷蟆。
             -->
            <!-- 讀取數(shù)據(jù)方法,一般采用只讀事務-->
            <tx:method name="get*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="load*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="select*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="list*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="query*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>
            <tx:method name="criteria*" isolation="DEFAULT"
                       propagation="SUPPORTS" read-only="true"/>


            <!--其他方法腐魂,如save帐偎,update,insert等對數(shù)據(jù)庫進行寫入操作的方法蛔屹,當產(chǎn)生ServiceException進行回滾 -->
            <tx:method name="init*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="insert*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="update*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="save*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="add*" isolation="DEFAULT" read-only="false"
                       propagation="REQUIRED" rollback-for="ServiceException"/>
            <tx:method name="create*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="del*" isolation="DEFAULT" read-only="false"
                       propagation="REQUIRED" rollback-for="ServiceException"/>
            <tx:method name="remove*" isolation="DEFAULT"
                       read-only="false" propagation="REQUIRED"
                       rollback-for="ServiceException"/>
            <tx:method name="process" isolation="DEFAULT" read-only="false"
                       propagation="REQUIRED" rollback-for="ServiceException"/>
        </tx:attributes>
    </tx:advice>

    <!--
        Spring采用AOP進行事務控制,這里指定了凡是實現(xiàn)了以com.tp打頭的包及其子包里以Service結(jié)尾接口中的所有方法需要由事務進行控制
    -->
    <aop:config>
        <aop:pointcut id="serviceOperation"
                      expression="execution(* com.tp..*Service.*(..))"/>
        <aop:advisor pointcut-ref="serviceOperation"
                     advice-ref="txAdvice"/>
    </aop:config>
</beans>

將此配置文件加入到Spring配置文件中削樊,我們使用UserServiceImpl中的saveUsere方法來測試聲明式事物:

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public User getById(Integer id) {
        if (null != id && id > 0) {
            return userMapper.getById(id);
        }
        return null;
    }

    @Override
    public int saveUser(User user) {
        if (null != user) {
            user.setCreateTime(new Date());
            int i = userMapper.saveUser(user);
            // 測試Spring聲明式事物
            // i = 2/0;
            return i;
        }
        return 0;
    }
}

我們會發(fā)現(xiàn)當我們將int i = 2/0;注釋掉時數(shù)據(jù)庫能夠成功保存用戶,而將其放開時無法事物會回滾兔毒,數(shù)據(jù)庫中不會保存用戶

4.2基于注解的聲明式事物配置

Spring支持注解方式的聲明式事物配置方式:在需要添加事務的方法上漫贞,添加 @Transactional 注解,表示該方法開啟事務眼刃,當然绕辖,這個注解也可以放在類上,表示這個類中的所有方法都開啟事務擂红。
如果要開啟 Java 注解配置仪际,在 XML 配置中添加如下配置

<tx:annotation-driven transaction-manager="transactionManager" />

這行配置,相當于上面XML配置中如下兩個配置:

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- 需要由交給spring的aop來進行代理的方法的集合昵骤,如果應用有自己的方法需有由spring來進行事務控制必須添加方法-->
        <!--
            PROPAGATION_REQUIRED:支持當前事務树碱,如果當前沒有事務,就新建一個事務变秦。這是最常見的選擇成榜。
            PROPAGATION_SUPPORTS:支持當前事務,如果當前沒有事務蹦玫,就以非事務方式執(zhí)行赎婚。
            PROPAGATION_MANDATORY:支持當前事務,如果當前沒有事務樱溉,就拋出異常挣输。
            PROPAGATION_REQUIRES_NEW:新建事務,如果當前存在事務福贞,把當前事務掛起撩嚼。
            PROPAGATION_NOT_SUPPORTED:以非事務方式執(zhí)行操作,如果當前存在事務,就把當前事務掛起完丽。
            PROPAGATION_NEVER:以非事務方式執(zhí)行恋技,如果當前存在事務,則拋出異常逻族。
            PROPAGATION_NESTED:支持當前事務蜻底,如果當前事務存在,則執(zhí)行一個嵌套事務瓷耙,如果當前沒有事務朱躺,就新建一個事務。
         -->
        <!-- 讀取數(shù)據(jù)方法搁痛,一般采用只讀事務-->
        ......
</tx:advice>

<!--
    Spring采用AOP進行事務控制,這里指定了凡是實現(xiàn)了以com.tp打頭的包及其子包里以Service結(jié)尾接口中的所有方法需要由事務進行控制
-->
<aop:config>
   ......
</aop:config>

然后长搀,添加@Transactional注解:

@Override
@Transactional
public int saveUser(User user) {
    if (null != user) {
        user.setCreateTime(new Date());
        int i = userMapper.saveUser(user);
        // 測試Spring聲明式事物
        // i = 2/0;
        return i;
    }
    return 0;
}

重新測試,效果一致鸡典。
參考文章:https://blog.csdn.net/trigl/article/details/50968079

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末源请,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子彻况,更是在濱河造成了極大的恐慌谁尸,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件纽甘,死亡現(xiàn)場離奇詭異良蛮,居然都是意外死亡,警方通過查閱死者的電腦和手機悍赢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門决瞳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人左权,你說我怎么就攤上這事皮胡。” “怎么了赏迟?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵屡贺,是天一觀的道長。 經(jīng)常有香客問我锌杀,道長甩栈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任糕再,我火速辦了婚禮谤职,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘亿鲜。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布蒿柳。 她就那樣靜靜地躺著饶套,像睡著了一般。 火紅的嫁衣襯著肌膚如雪垒探。 梳的紋絲不亂的頭發(fā)上妓蛮,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 那天,我揣著相機與錄音圾叼,去河邊找鬼蛤克。 笑死,一個胖子當著我的面吹牛夷蚊,可吹牛的內(nèi)容都是我干的构挤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼惕鼓,長吁一口氣:“原來是場噩夢啊……” “哼筋现!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起箱歧,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤矾飞,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后呀邢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體洒沦,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年价淌,在試婚紗的時候發(fā)現(xiàn)自己被綠了申眼。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡输钩,死狀恐怖豺型,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情买乃,我是刑警寧澤姻氨,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站剪验,受9級特大地震影響肴焊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜功戚,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一娶眷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧啸臀,春花似錦届宠、人聲如沸烁落。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽伤塌。三九已至,卻和暖如春轧铁,著一層夾襖步出監(jiān)牢的瞬間每聪,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工齿风, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留药薯,地道東北人。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓救斑,卻偏偏與公主長得像童本,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子系谐,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 事務的嵌套概念 所謂事務的嵌套就是兩個事務方法之間相互調(diào)用巾陕。spring事務開啟 ,或者是基于接口的或者是基于類的...
    jackcooper閱讀 1,423評論 0 10
  • 轉(zhuǎn)載自:http://www.reibang.com/p/8ddc01f23540 Spring事務機制主要包括...
    星海辰光大人閱讀 347評論 0 0
  • 不足的地方請大家多多指正纪他,如有其它沒有想到的常問面試題請大家多多評論鄙煤,一起成長,感謝!~ String可以被繼承嗎...
    啟示錄是真的閱讀 2,943評論 3 3
  • 事務的嵌套概念 所謂事務的嵌套就是兩個事務方法之間相互調(diào)用茶袒。spring事務開啟 梯刚,或者是基于接口的或者是基于類的...
    pigstomachs閱讀 1,023評論 0 1
  • 最近再次拜讀了spring的源碼,對關于spring事物的應用部分加以整理,做如下筆記薪寓。如有不正確的地方亡资,歡迎指正...
    默寫流年閱讀 2,017評論 0 7