1. 程式人生 > >Spring事務管理 與 SpringAOP

Spring事務管理 與 SpringAOP

nsf 新建 spring事務 釋放資源 pre pan 基本配置 特定 details

1,Spring事務的核心接口

  Spring事務管理的實現有許多細節,如果對整個接口框架有個大體了解會非常有利於我們理解事務,下面通過講解Spring的事務接口來了解Spring實現事務的具體策略。
  Spring事務管理涉及的接口的聯系如下:

技術分享圖片

1.1 事務管理器

Spring並不直接管理事務,而是提供了多種事務管理器,他們將事務管理的職責委托給Hibernate或者JTA等持久化機制所提供的相關平臺框架的事務來實現。
Spring事務管理器的接口是org.springframework.transaction.PlatformTransactionManager,通過這個接口,Spring為各個平臺如JDBC、Hibernate等都提供了對應的事務管理器,但是具體的實現就是各個平臺自己的事情了。此接口的內容如下:

Public interface PlatformTransactionManager()...{  
    // 由TransactionDefinition得到TransactionStatus對象 
    TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; 
    // 提交
    Void commit(TransactionStatus status) throws TransactionException;  
    // 回滾
    Void rollback(TransactionStatus status) throws
TransactionException; }

從這裏可知具體的具體的事務管理機制對Spring來說是透明的,它並不關心那些,那些是對應各個平臺需要關心的,所以Spring事務管理的一個優點就是為不同的事務API提供一致的編程模型,如JTA、JDBC、Hibernate、JPA。下面分別介紹各個平臺框架實現事務管理的機制。

1.2 JDBC事務

如果應用程序中直接使用JDBC來進行持久化,DataSourceTransactionManager會為你處理事務邊界。為了使用DataSourceTransactionManager,你需要使用如下的XML將其裝配到應用程序的上下文定義中:

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

實際上,DataSourceTransactionManager是通過調用java.sql.Connection來管理事務,而後者是通過DataSource獲取到的。通過調用連接的commit()方法來提交事務,同樣,事務失敗則通過調用rollback()方法進行回滾。

1.3 Java原生API事務

如果你沒有使用以上所述的事務管理,或者是跨越了多個事務管理源(比如兩個或者是多個不同的數據源),你就需要使用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,基本事務屬性的定義

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

技術分享圖片

而TransactionDefinition接口內容如下:

public interface TransactionDefinition {
    int getPropagationBehavior(); // 返回事務的傳播行為
    int getIsolationLevel(); // 返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些數據
    int getTimeout();  // 返回事務必須在多少秒內完成
    boolean isReadOnly(); // 事務是否只讀,事務管理器能夠根據這個返回值進行優化,確保事務是只讀的
} 

我們可以發現TransactionDefinition正好用來定義事務屬性,下面詳細介紹一下各個事務屬性。

2.1 傳播行為

事務的第一個方面是傳播行為(propagation behavior)。當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啟一個新事務,並在自己的事務中運行。Spring定義了七種傳播行為:

傳播行為含義
PROPAGATION_REQUIRED 表示當前方法必須運行在事務中。如果當前事務存在,方法將會在該事務中運行。否則,會啟動一個新的事務
PROPAGATION_SUPPORTS 表示當前方法不需要事務上下文,但是如果存在當前事務的話,那麽該方法會在這個事務中運行
PROPAGATION_MANDATORY 表示該方法必須在事務中運行,如果當前事務不存在,則會拋出一個異常
PROPAGATION_REQUIRED_NEW 表示當前方法必須運行在它自己的事務中。一個新的事務將被啟動。如果存在當前事務,在該方法執行期間,當前事務會被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager
PROPAGATION_NOT_SUPPORTED 表示該方法不應該運行在事務中。如果存在當前事務,在該方法運行期間,當前事務將被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager
PROPAGATION_NEVER 表示當前方法不應該運行在事務上下文中。如果當前正有一個事務在運行,則會拋出異常
PROPAGATION_NESTED 表示如果當前已經存在一個事務,那麽該方法將會在嵌套事務中運行。嵌套的事務可以獨立於當前事務進行單獨地提交或回滾。如果當前事務不存在,那麽其行為與PROPAGATION_REQUIRED一樣。註意各廠商對這種傳播行為的支持是有所差異的。可以參考資源管理器的文檔來確認它們是否支持嵌套事務

註:以下具體講解傳播行為的內容參考自Spring事務機制詳解
(1)PROPAGATION_REQUIRED 如果存在一個事務,則支持當前事務。如果沒有事務則開啟一個新的事務。

//事務屬性 PROPAGATION_REQUIRED
methodA{
    ……
    methodB();
    ……
}
//事務屬性 PROPAGATION_REQUIRED
methodB{
   ……
}

使用spring聲明式事務,spring使用AOP來支持聲明式事務,會根據事務屬性,自動在方法調用之前決定是否開啟一個事務,並在方法執行之後決定事務提交或回滾事務。

單獨調用methodB方法:

main{ 
    metodB(); 
}  

相當於

Main{ 
    Connection con=null; 
    try{ 
        con = getConnection(); 
        con.setAutoCommit(false); 

        //方法調用
        methodB(); 

        //提交事務
        con.commit(); 
    } Catch(RuntimeException ex) { 
        //回滾事務
        con.rollback();   
    } finally { 
        //釋放資源
        closeCon(); 
    } 
} 

Spring保證在methodB方法中所有的調用都獲得到一個相同的連接。在調用methodB時,沒有一個存在的事務,所以獲得一個新的連接,開啟了一個新的事務。
單獨調用MethodA時,在MethodA內又會調用MethodB.

執行效果相當於:

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

調用MethodA時,環境中沒有事務,所以開啟一個新的事務.當在MethodA中調用MethodB時,環境中已經有了一個事務,所以methodB就加入當前事務。

(2)PROPAGATION_SUPPORTS 如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行。但是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。

//事務屬性 PROPAGATION_REQUIRED
methodA(){
  methodB();
}

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

單純的調用methodB時,methodB方法是非事務的執行的。當調用methdA時,methodB則加入了methodA的事務中,事務地執行。

(3)PROPAGATION_MANDATORY 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常

技術分享圖片
//事務屬性 PROPAGATION_REQUIRED
methodA(){
    methodB();
}

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

當單獨調用methodB時,因為當前沒有一個活動的事務,則會拋出異常throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);當調用methodA時,methodB則加入到methodA的事務中,事務地執行。

(4)PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。

//事務屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

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

調用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執行完後,恢復第一個事務
        tm.resume(ts1);
        doSomeThingB();
        ts1.commit();//提交第一個事務
    } catch(RunTimeException ex) {
        ts1.rollback();//回滾第一個事務
    } finally {
        //釋放資源
    }
}
View Code

在這裏,我把ts1稱為外層事務,ts2稱為內層事務。從上面的代碼可以看出,ts2與ts1是兩個獨立的事務,互不相幹。Ts2是否成功並不依賴於 ts1。如果methodA方法在調用methodB方法後的doSomeThingB方法失敗了,而methodB方法所做的結果依然被提交。而除了 methodB之外的其它代碼導致的結果卻被回滾了。使用PROPAGATION_REQUIRES_NEW,需要使用 JtaTransactionManager作為事務管理器。

(5)PROPAGATION_NOT_SUPPORTED 總是非事務地執行,並掛起任何存在的事務。使用PROPAGATION_NOT_SUPPORTED,也需要使用JtaTransactionManager作為事務管理器。(代碼示例同上,可同理推出)

(6)PROPAGATION_NEVER 總是非事務地執行,如果存在一個活動事務,則拋出異常

(7)PROPAGATION_NESTED如果一個活動的事務存在,則運行在一個嵌套的事務中. 如果沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager作為事務管理器。需要JDBC 驅動的java.sql.Savepoint類。有一些JTA的事務管理器實現可能也提供了同樣的功能。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true;而 nestedTransactionAllowed屬性值默認為false。

//事務屬性 PROPAGATION_REQUIRED
methodA(){
    doSomeThingA();
    methodB();
    doSomeThingB();
}

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

如果單獨調用methodB方法,則按REQUIRED屬性執行。如果調用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方法調用之前,調用setSavepoint方法,保存當前的狀態到savepoint。如果methodB方法調用失敗,則恢復到之前保存的狀態。但是需要註意的是,這時的事務並沒有進行提交,如果後續的代碼(doSomeThingB()方法)調用失敗,則回滾包括methodB方法的所有操作

  嵌套事務一個非常重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所做的動作。而內層事務操作失敗並不會引起外層事務的回滾

  PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別:它們非常類似,都像一個嵌套事務,如果不存在一個活動的事務,都會開啟一個新的事務。使用 PROPAGATION_REQUIRES_NEW時,內層事務與外層事務就像兩個獨立的事務一樣,一旦內層事務進行了提交後,外層事務不能對其進行回滾。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它需要JTA事務管理器的支持。

  使用PROPAGATION_NESTED時,外層事務的回滾可以引起內層事務的回滾。而內層事務的異常並不會導致外層事務的回滾,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED時,需要JDBC 3.0以上驅動及1.4以上的JDK版本支持。其它的JTA TrasactionManager實現可能有不同的支持方式。

  PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴於環境的 “內部” 事務. 這個事務將被完全 commited 或 rolled back 而不依賴於外部事務, 它擁有自己的隔離範圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行。

  另一方面, PROPAGATION_NESTED 開始一個 “嵌套的” 事務, 它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, 它將取得一個 savepoint. 如果這個嵌套事務失敗, 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束後它才會被提交。

  由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於, PROPAGATION_REQUIRES_NEW 完全是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 如果外部事務 commit, 嵌套事務也會被 commit, 這個規則同樣適用於 roll back.

PROPAGATION_REQUIRED應該是我們首先的事務傳播行為。它能夠滿足我們大多數的事務需求。

2.2隔離級別

事務的第二個維度就是隔離級別(isolation level)。隔離級別定義了一個事務可能受其他並發事務影響的程度。
(1)並發事務引起的問題
在典型的應用程序中,多個事務並發運行,經常會操作相同的數據來完成各自的任務。並發雖然是必須的,但可能會導致一下的問題。

  • 臟讀(Dirty reads)——臟讀發生在一個事務讀取了另一個事務改寫但尚未提交的數據時。如果改寫在稍後被回滾了,那麽第一個事務獲取的數據就是無效的。
  • 不可重復讀(Nonrepeatable read)——不可重復讀發生在一個事務執行相同的查詢兩次或兩次以上,但是每次都得到不同的數據時。這通常是因為另一個並發事務在兩次查詢期間進行了更新。
  • 幻讀(Phantom read)——幻讀與不可重復讀類似。它發生在一個事務(T1)讀取了幾行數據,接著另一個並發事務(T2)插入了一些數據時。在隨後的查詢中,第一個事務(T1)就會發現多了一些原本不存在的記錄。

不可重復讀與幻讀的區別:

1)不可重復讀的重點是修改

同樣的條件, 你讀取過的數據, 再次讀取出來發現值不一樣了
例如:在事務1中,Mary 讀取了自己的工資為1000,操作並沒有完成

con1 = getConnection();  
    select salary from employee empId ="Mary";  

在事務2中,這時財務人員修改了Mary的工資為2000,並提交了事務.

  con2 = getConnection();  
    update employee set salary = 2000;  
    con2.commit();  

在事務1中,Mary 再次讀取自己的工資時,工資變為了2000

 //con1  
    select salary from employee empId ="Mary"; 

在一個事務中前後兩次讀取的結果並不一致,導致了不可重復讀。

2)幻讀的重點在於新增或者刪除:
同樣的條件, 第1次和第2次讀出來的記錄數不一樣
例如:目前工資為1000的員工有10人。事務1,讀取所有工資為1000的員工。

con1 = getConnection();  
    Select * from employee where salary =1000; 

共讀取10條記錄

這時另一個事務向employee表插入了一條員工記錄,工資也為1000

 con2 = getConnection();  
    Insert into employee(empId,salary) values("Lili",1000);  
    con2.commit();  

事務1再次讀取所有工資為1000的員工

 //con1  
    select * from employee where salary =1000;  

共讀取到了11條記錄,這就產生了幻像讀。

從總的結果來看, 似乎不可重復讀和幻讀都表現為兩次讀取的結果不一致。但如果你從控制的角度來看, 兩者的區別就比較大。
對於前者, 只需要鎖住滿足條件的記錄。
對於後者, 要鎖住滿足條件及其相近的記錄。

(2)隔離級別

隔離級別含義
ISOLATION_DEFAULT 使用後端數據庫默認的隔離級別
ISOLATION_READ_UNCOMMITTED 最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀
ISOLATION_READ_COMMITTED 允許讀取並發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生
ISOLATION_REPEATABLE_READ 對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生
ISOLATION_SERIALIZABLE 最高的隔離級別,完全服從ACID的隔離級別,確保阻止臟讀、不可重復讀以及幻讀,也是最慢的事務隔離級別,因為它通常是通過完全鎖定事務相關的數據庫表來實現的

2.3 只讀

事務的第三個特性是它是否為只讀事務。如果事務只對後端的數據庫進行該操作,數據庫可以利用事務的只讀特性來進行一些特定的優化。通過將事務設置為只讀,你就可以給數據庫一個機會,讓它應用它認為合適的優化措施。

2.4 事務超時

為了使應用程序很好地運行,事務不能運行太長的時間。因為事務可能涉及對後端數據庫的鎖定,所以長時間的事務會不必要的占用數據庫資源。事務超時就是事務的一個定時器,在特定時間內事務如果沒有執行完畢,那麽就會自動回滾,而不是一直等待其結束。

2.5 回滾規則

事務五邊形的最後一個方面是一組規則,這些規則定義了哪些異常會導致事務回滾而哪些不會。默認情況下,事務只有遇到運行期異常時才會回滾,而在遇到檢查型異常時不會回滾(這一行為與EJB的回滾行為是一致的)
但是你可以聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。同樣,你還可以聲明事務遇到特定的異常不回滾,即使這些異常是運行期異常。

2.6 事務狀態

上面講到的調用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現,這個接口的內容如下:

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

可以發現這個接口描述的是一些處理事務提供簡單的控制事務執行和查詢事務狀態的方法,在回滾或提交的時候需要應用對應的事務狀態。

3,編程式事務和聲明式事務

  Spring提供了對編程式事務聲明式事務的支持,編程式事務允許用戶在代碼中精確定義事務的邊界,而聲明式事務(基於AOP)有助於用戶將操作與事務規則進行解耦。

  簡單地說,編程式事務侵入到了業務代碼裏面,但是提供了更加詳細的事務管理;而聲明式事務由於基於AOP,所以既能起到事務管理的作用,又可以不影響業務代碼的具體實現。

3.1編程式事務

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

3.1 .1使用TransactionTemplate

采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一樣的方法。它使用回調方法,把應用程序從處理取得和釋放資源中解脫出來。如同其他模板,TransactionTemplate是線程安全的。代碼片段:

 TransactionTemplate tt = new TransactionTemplate(); // 新建一個TransactionTemplate
    Object result = tt.execute(
        new TransactionCallback(){  
            public Object doTransaction(TransactionStatus status){  
                updateOperation();  
                return resultOfUpdateOperation();  
            }  
    }); // 執行execute方法進行事務管理

使用TransactionCallback()可以返回一個值。如果使用TransactionCallbackWithoutResult則沒有返回值。

3.2.2 使用PlatformTransactionManager

示例代碼如下:

 DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定義一個某個框架平臺的TransactionManager,如JDBC、Hibernate
    dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 設置數據源
    DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定義事務屬性
    transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 設置傳播行為屬性
    TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 獲得事務狀態
    try {
        // 數據庫操作
        dataSourceTransactionManager.commit(status);// 提交
    } catch (Exception e) {
        dataSourceTransactionManager.rollback(status);// 回滾
    }

3.2 聲明式事務

3.2.1 Spring聲明式事務與SpringAOP的說明

1)Spring aop的底層實現是采用的動態代理實現的;

2)當目標類有接口和實現類的時候Spring采用jdk動態代理實現,當只有實現類的時候Spring采用cglib字節碼增強的方式實現

3)AspectJ是一個基於Java語言的AOP框架,底層實現還是動態代理(即2中的兩種代理方式),使用方式兩種:基於xml和基於註解。

4)當然不使用AspectJ框架,Spring也可以實現aop也可以使用基於xml的聲明式事務,用註冊bean,往bean裏註入屬性的方法半自動實現,比較麻煩,所以平時都用AspectJ。

3.2.2 Spring聲明式事務五種配置方式

5種方式;http://www.blogjava.net/robbie/archive/2009/04/05/264003.html

我們主要用的就是基於AspectJ框架的兩種

第一種:基於xml的,使用使用tx標簽配置的攔截器

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

    <!-- 事務配置 -->
    <!-- 事務管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 數據源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 傳播行為 -->
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="create*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>
    <!-- 切面 -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice"
            pointcut="execution(* com.taotao.service.*.*(..))" />
    </aop:config>
</beans>

這個配置(聲明)的意思就是攔截以save、insert、add等開頭的方法,按照後面的屬性(這裏配了傳播行為給他們添加事務(通知)。

第二種:用註解@Transactional。

配置文件

<?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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:cache="http://www.springframework.org/schema/cache" xmlns:repo="http://www.springframework.org/schema/data/repository"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
        http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
        http://www.springframework.org/schema/data/repository http://www.springframework.org/schema/data/repository/spring-repository-1.7.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
            http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd "
    default-lazy-init="true">
 
    <description>Spring配置</description>
    <context:annotation-config />
<!-- 引入數據源配置文件 -->
<context:property-placeholder location="classpath:jdbc.properties"/>
  
  <!-- 連接池 -->
  <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
   <property name="driverClass" value="${jdbc.driverClass}"></property>
    <property name="jdbcUrl" value="${jdbc.url}"></property>
     <property name="user" value="${jdbc.username}"></property>
      <property name="password" value="${jdbc.password}"></property>
  </bean>
  
  
  <!-- 持久層 -->
  <bean id="userAccountDao" class="com.trans.trans04.UserAccountDao"> 
      <property name="dataSource" ref="dataSource"></property>
  </bean>
  
  <bean id="userAccoutService" class="com.trans.trans04.UserAccoutService">
  </bean>
  
  <!-- 配置事務管理其 -->
  
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 註入數據連接池 -->
        <property name="dataSource" ref="dataSource"></property>
  </bean>
  
  <!-- 開啟註解事務 -->
  <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

註解使用

package com.trans.trans04;
 
import javax.annotation.Resource;
 
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
 
 
@Transactional(propagation=Propagation.REQUIRED)
public class UserAccoutService implements IUserAccoutService {
 
    @Resource(name="userAccountDao")
    private IUserAccountDao userAccountDao; 
    
    public void UserTransferMoney(String fromAccount,String toAccount,
             double money) {
            //  org.springframework.transaction.TransactionDefinition.ISOLATION_REPEATABLE_READ
                userAccountDao.TransferOut(fromAccount, money);//轉出賬戶
                int m=5/0; //出現異常
                userAccountDao.TransferIn(toAccount, money);//轉入賬戶
    }
}

Spring事務管理詳解:https://blog.csdn.net/trigl/article/details/50968079

註解事務實例:https://blog.csdn.net/hsgao_water/article/details/52860380

更詳細的實驗:https://blog.csdn.net/zx13525079024/article/details/52075784

Spring事務管理 與 SpringAOP