1. 程式人生 > 其它 >Spring:事務管理

Spring:事務管理

技術標籤:Spring

Spring的事務管理不需要與任何特定的事務API耦合。對不同的持久層訪問技術,程式設計式事務提供了一致的事務程式設計風格,通過模板化操作一致性地管理事務。宣告式事務基於Spring AOP實現,但並不需要開發者真正精通AOP技術,即可容易地使用Spring的宣告式事務管理。

1,Spring支援的事務策略

1.1,傳統的事務策略

Java EE應用的傳統事務有兩個策略:全域性事務和區域性事務。

  • 全域性事務由應用伺服器管理,需要底層伺服器的JTA支援。可以跨多個事務資源(典型的例子是關係資料庫和訊息佇列);使用區域性事務,應用伺服器不需要參與事務管理,因此不能保證跨多個事務性資源的事務的正確性。
  • 區域性事務和底層所採用的持久化技術有關,當採用JDBC持久化技術時,需要使用Connection物件來操作事務。而採用Hibernate持久化技術時,需要使用Session物件來操作事務。

JTA全域性事務、JDBC區域性事務、Hibernate事務的事務操作程式碼:

JTA全域性事務JDBC區域性事務Hibernate事務
Transaction tx = ctx.lookup();

Connection conn = getConnection();

conn.setAutoCommit(false);

Session s = getSession();

Transaction tx = s.beginTransaction;

//業務實現

if 正常

tx.commit();

if 失敗

tx.rollback();

if 正常

conn.commit();

if 失敗

conn.rollback();

if 正常

tx.commit();

if 失敗

tx.rollback();

事務結束

當採用傳統的事務程式設計策略時,程式程式碼必然和具體的事務操作程式碼耦合,這樣造成的後果是:當應用需要在不同的事務策略之間切換時,開發者必須手動修改程式程式碼。如果使用Spring事務管理,就可以改變這種現狀。

1.2,PlatformTransactionManager

Spring事務策略是通過PlatformTransactionManager

介面體現的,該介面是Spring事務策略的核心。該介面的原始碼如下:

public interface PlatformTransactionManager extends TransactionManager {
    //平臺無關的獲得事務方法
    TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;
    //平臺無關的事務提交方法
    void commit(TransactionStatus var1) throws TransactionException;
    //平臺無關的事務回滾方法
    void rollback(TransactionStatus var1) throws TransactionException;
}

PlatformTransactionManager是一個與任何事務策略分離的介面,隨著底層不同事務策略的切換,應用必須採用不同的實現類。PlatformTransactionManager介面沒有與任何事務性資源捆綁在一起,它可以適應於任何事務策略,結合Spring的IoC容器,可以向PlatformTransactionManager注入相關的平臺特性。

PlatformTransactionManager介面有許多不同的實現類,應用程式面向與平臺無關的介面程式設計,當底層採用不同的持久化層技術時,系統只需使用不同的PlatformTransactionManager實現類即可——而這種切換通常由Spring容器負責管理,應用程式無須與具體的事務API耦合,也無須與特定實現類耦合,從而將應用和持久化技術、事務API徹底分離開來。

Spring的事務機制使一種典型的策略模式,PlatformTransactionManager代表事務管理介面,但它並不知道底層是如何管理事務,它只要求事務管理需要提供開始事務(getTransaction())、提交事務(commit())和回滾事務(rollbackj())方法,但具體如何實現則交給其他實現類完成——不同的實現類則代表不同的事務管理策略。

在PlatformTransactionManager介面內,包含一個getTransaction(TransactionDefinition definition)方法,該方法根據TransactionDefinition引數返回一個TransactionStatus物件。TransactionStatus物件表示一個事務,TransactionStatus被關聯在當前執行的執行緒上。

getTransaction(TransactionDefinition definition)返回的TransactionStatus物件,可能是一個新的事務,也可能是一個已經存在的事務物件。如果當前執行的執行緒已經處於事務管理下,則返回當前執行緒的事務物件;否則,系統將新建一個事務物件後返回。

TransactionDefinition介面定義了一個事務規則,該介面必須指定如下幾個屬性:

  • 事務隔離:當前事務和其他事務的隔離程度。例如,這個事務能否看到其他事務未提交的資料等。
  • 事務傳播:通常,在事務中執行的程式碼都會在當前事務中執行。但是,如果一個事務上下文已經存在,有幾個選項可指定該事務性方法的執行行為。例如,在大多數情況下,簡單地在現有的事務上下文中執行;或者掛起現有事務,建立一個新的事務。
  • 事務超時:事務在超時前能執行多久,也就是事務的最長持續時間。如果事務一直沒有被提交和回滾,將在超出該事件後,系統自動回滾事務。
  • 只讀狀態:只讀事務不修改任何資料。在某些情況下(例如使用HIbernate時),只讀事務是非常有用的優化。

TransactionStatus代表事務本身,它提供了簡單的控制事務執行和查詢事務狀態的方法,這些方法在所有的事務API中都是相同的。

public interface TransactionExecution {
    //判斷事務是否為新建的事務
    boolean isNewTransaction();
    //設定事務回滾
    void setRollbackOnly();
    //判斷是否已回滾
    boolean isRollbackOnly();
    //判斷是否提交完成
    boolean isCompleted();
}

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
    boolean hasSavepoint();

    void flush();
}

Spring具體的事務管理由PlatformTransactionmanager的不同實現類完成。在Spring容器中配置PlatformTransactionManagerBean時,必須針對不同的環境提供不同的實現類。

1.3,不同持久層訪問環境及其對應的PlatformTransactionManager實現類的配置。

容器管理的JTA全域性事務管理器的配置

<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!--配置JNDI資料來源Bean,其中jndiName指定容器管理資料來源的JNDI-->
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"
          p:jndiName="jdbc/jpetstore"/>
    <!--使用JtaTransactionManager類,該類實現了PlatformTransactionManager介面-->
    <!--針對採用全域性事務管理的特定實現-->
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
</beans>

當配置JtaTransactionManager全域性事務管理策略時,只需指定事務管理器實現類即可,無須傳入額外的事務性資源。這是因為全域性事務的JTA資源由Java EE伺服器提供,而Spring容器能自行從Java EE伺服器中獲取該事務性資源,所以無須使用依賴注入來配置。

JDBC資料來源的區域性事務管理的配置

<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!--定義資料來源Bean,使用C3P0資料來源實現,並注入資料來源的必要資訊-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
          p:driverClass="com.mysql.jdbc.Driver"
          p:jdbcUrl="jdbc:mysql://localhost/spring"
          p:user="root"
          p:password="123456"
          p:maxPoolSize="40"
          p:minPoolSize="2"
          p:initialPoolSize="2"
          p:maxIdleTime="30"/>
    <!--配置JDBC的區域性事務管理器,使用DataSourceTransactionManager類-->
    <!--該類實現PlatformTransactionManager介面,是針對採用資料來源連線的特定實現-->
    <!--配置DataSourceTransactionManager時需要依賴注入DataSource的引用-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
</beans>

Hibernate持久層訪問策略,區域性事務策略的配置

<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!--定義資料來源Bean,使用C3P0資料來源實現,並注入資料來源的必要資訊-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
          p:driverClass="com.mysql.jdbc.Driver"
          p:jdbcUrl="jdbc:mysql://localhost/spring"
          p:user="root"
          p:password="123456"
          p:maxPoolSize="40"
          p:minPoolSize="2"
          p:initialPoolSize="2"
          p:maxIdleTime="30"/>
    <!--定義Hibernate的SessionFactory,SessionFactory需要依賴資料來源,注入dataSource-->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"
          p:dataSource-ref="dataSource">
        <!--annotatedClasses用來列出全部持久化類-->
        <property name="annotatedClasses">
            <list>
                <!--以下用來列出所有的PO類-->
                <value>Pojo.User</value>
            </list>
        </property>
        <!--定義Hibernate SessionFactory的屬性-->
        <property name="hibernateProperties">
            <props>
                <!--指定Hibernate的連線方言-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MYSQL5InnoDBDialect</prop>
                <!--是否根據Hibernate對映建立資料表-->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                
            </props>
        </property>
    </bean>
    <!--配置Hibernate的區域性事務管理器,使用HibernateTransactionManager類-->
    <!--該類是PlatformTransactionManager介面針對採用Hibernate的特定實現類-->
    <!--配置HibernateTransactionManager需要依賴注入SessionFactory-->
    <bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager"
          p:sessionFactory-ref="sessionFactory"/>
</beans>

如果底層採用的是Hibernate持久層技術,但事務採用JTA全域性事務,則Spring配置檔案如下:

<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context"
       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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!--配置JNDI資料來源Bean,其中jndiName指定容器管理資料來源的JNDI-->
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"
          p:jndiName="jdbc/jpetstore"/>
     <!--定義Hibernate的SessionFactory,SessionFactory需要依賴資料來源,注入dataSource-->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean"
          p:dataSource-ref="dataSource">
        <!--annotatedClasses用來列出全部持久化類-->
        <property name="annotatedClasses">
            <list>
                <!--以下用來列出所有的PO類-->
                <value>Pojo.User</value>
            </list>
        </property>
        <!--定義Hibernate SessionFactory的屬性-->
        <property name="hibernateProperties">
            <props>
                <!--指定Hibernate的連線方言-->
                <prop key="hibernate.dialect">org.hibernate.dialect.MYSQL5InnoDBDialect</prop>
                <!--是否根據Hibernate對映建立資料表-->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
                
            </props>
        </property>
    </bean>
    <!--使用JtaTransactionManager類,該類實現了PlatformTransactionManager介面-->
    <!--針對採用全域性事務管理的特定實現-->
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
</beans>

從配置可以看出,不論採用哪種持久層訪問技術,只要使用JTA去全域性事務,Spring事務管理的配置就完全一樣,因為它們採用的全都是全域性事務管理策略。

當程式採用Spring事務管理策略時,應用程式無須與具體的事務策略耦合,應用程式只要面向PlatformTransactionManager策略介面程式設計,ApplicationContext將會根據配置檔案選擇合適的事務策略實現類。

實際上,Spring提供瞭如下兩種事務管理方式:

  • 程式設計式事務管理:即使使用Spring的程式設計式事務,程式也可直接獲取容器中的transacntionManager Bean,該Bean總是PlatformTransactionManager的例項,所有可以通過該介面體用的三個方法來開始事務、提交事務和回滾事務。
  • 宣告式事務管理:無須在Java程式中書寫任何事務操作程式碼,而是通過在XML檔案中為業務組配置事務代理(AOP代理的一種),AOP為事務代理所織入的增強處理也由Spring提供——在目標方法執行之前,織入開始事務;在目標方法執行之後,織入結束事務。

不論採用何種持久化策略,Spring都提供了一致的事務抽象,因此,應用開發者能在任何環境下,使用一致的程式設計模型。無須更改程式碼,應用就可在不同的事務管理策略中切換。

(1)當使用程式設計式事務時,開發者使用的是Spring事務抽象,而無需使用任何具體的底層事務API。Spring的事務管理將程式碼從底層具體的事務API中抽象出來,該抽象能以任何底層事務為基礎。Spring的程式設計式事務還可通過TransactionTemplace類來完成,該類體用了一個execute方法,可以更簡潔的方式來進行事務操作。

(2)當使用宣告式事務時,開發者無須書寫任何事務管理程式碼,不依賴Spring或任何其他事務API。Spring的宣告式事務無須任何額外的容器支援,Spring容器本身管理宣告式事務。使用宣告式事務策略,可以讓開發者更好地專注於業務邏輯的實現。Spring所支援的事務策略非常靈活,Spring的事務策略允許應用程式在不同的事務策略之間自由切換,即使需要在區域性事務策略和全域性事務策略之間切換,也只需要修改配置檔案計科,而應用程式的程式碼無須任何改變,這種靈活的設計,正是面向介面程式設計帶來的優勢。

2,使用XML Schema配置事務策略(宣告式事務策略)

Spring同時支援程式設計式事務策略和宣告式事務策略,通常都推薦採用宣告式事務策略。使用宣告式事務策略的優勢十分明顯:

  • 宣告式事務能大大降低開發者的程式碼書寫數量,而且宣告式事務幾乎不影響應用的程式碼。因此,無論底層事務策略如何變化,應用程式都無須任何改變。
  • 應用程式程式碼無須任何事務處理程式碼,可以更專注於業務邏輯的實現。
  • Spring則可對任何POJO的方法提供事務管理,而且Spring的宣告式事務管理無須容器的支援,可在任何環境下使用。
  • EJB的CMT無法提供宣告式回滾規則;而通過配置檔案,Spring可指定事務在遇到特定異常時自動回滾。Spring不僅可以在程式碼中使用setRollbackOnly回滾事務,也可以在配置檔案中配置回滾規則。
  • 由於Spring採用AOP的方式管理事務,可以在事務回滾動作中插入使用者自己的動作,而不僅僅是執行系統預設的回滾。

2.1,配置方法

Spring的XML Schema方式提供了簡潔的事務配置策略,Spring2.x提供了tx:名稱空間來配置事務管理,tx:名稱空間下提供了<tx:advice.../>元素來配置事務增強處理,一旦使用該元素配置了事務增強增強處理,就可直接使用<aop:advisor.../>元素啟用自動代理。

配置<tx:advice.../>元素時除了需要transaction-manager屬性指定事務管理器之外,還需要配置一個<attributes.../>子元素,該子元素裡又可包含多個<method.../>子元素

配置<tx:advice.../>的重點就是配置<method.../>子元素,實際上每個<method.../>子元素都為一批方法指定了所需的事務定義,包括事務傳播屬性、事務隔離屬性、事務超時屬性、只讀事務、對指定異常回滾、對指定異常不會滾等。

配置<method.../>子元素可以指定如下幾個屬性:

  • name:必選屬性,與該事務語義關聯的方法名。該屬性支援使用萬用字元,例如'get*'、'handle*'、'on*Event'等。
  • propagation:指定事務傳播行為,該屬性值可為Propagation列舉類的前一列舉值。預設值為Propagation.REQUIRED。
  • isolation:指定事務隔離級別,該屬性可為Isolation列舉類的任一列舉值。該屬性的預設值為Isolation.DEFAULT。
  • timeout:指定事務超時的時間(以秒為單位),指定-1意味著不超時,該屬性的預設值是-1。
  • read-only:指定事務是否只讀。該屬性的預設值是false。
  • rollback-for:指定觸發事務回滾的異常類(應使用全限定類名),該屬性可指定多個異常類,多個異常類之間以英文逗號隔開。
  • no-rollback-for:指定不觸發事務回滾的異常類(應使用全限定類名),該屬性可指定多個異常類,多個異常類之間用英文逗號隔開。

2.2,隔離級別

<method.../>子元素的propagation屬性用於指定事務傳播行為,Spring支援事務傳播行為如下:

  • PROPAGATION_MANDATORY:要求呼叫該方法必須處於事務環境中,否則丟擲異常。
  • PROPAGATION_NESTED:即使執行該方法的執行緒已處於事物環境中,也依然啟動新的事務,方法在巢狀的事務裡執行;即使執行該方法的執行緒並未處於事務環境中,也啟動新的事務,然後執行該方法,此時PROPAGATION_REQIRED相同。
  • PROPAGATION_NEVER:不允許呼叫該方法的執行緒處於事務環境中,如果呼叫該方法的執行緒處於事務環境中,則丟擲異常。
  • PROPAGATION_NOT_SUPPORTED:如果呼叫該方法的執行緒處於事務環境中,則先暫停當前事務,然後執行該方法。
  • PROPAGATION_REQUIRED:要求在事務環境中執行該方法,如果當前執行執行緒已處於事務環境中,則直接呼叫;如果當前執行執行緒不處於事務環境中,則啟動新的事務環境中,則啟動新的事務後執行該方法。
  • PROPAGATION_REQUIRES_NEW:該方法要求在新的事務環境中執行,如果當前執行執行緒已處於事務環境中,則先暫停當前事務,啟動新事務後執行該方法;如果當前呼叫執行緒不處於事物環境中,則啟動新的事務後執行方法。
  • PROPAGATION_SUPPORTS:如果當前執行執行緒處於事務管敬仲,則使用當前事務,否則不使用事務。

2.3,事務案例

本例使用NewsDao元件來測試Spring的事務功能,程式將使用<tx:advice.../>元素來配置事務增強處理,再使用<aop:advisor.../>為容器中的一批Bean配置自動事務代理。

NewDao元件包含一個insert()方法,該方法同時插入兩條記錄,但插入第二條記錄將會違反唯一性約束,引發異常。

package Dao;

import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;

public class NewsDao {
    private DataSource ds;

    public void setDs(DataSource ds) {
        this.ds = ds;
    }
    public void insert(String title,String content){
        JdbcTemplate jt = new JdbcTemplate(ds);
        jt.update("insert into sutdent values(1,?,?)",title,content);
        jt.update("insert into sutdent values(1,?,?)",title,content);
    }
}
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p"
       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-4.0.xsd
     http://www.springframework.org/schema/context
     https://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/aop
     htpp://www.springframework.org/schema/tx
     htpp://www.springframework.org/schema/tx/spring-tx-4.0xsd
     https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--定義資料來源Bean,使用C3P0資料來源實現,並注入資料來源的必要資訊-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
          p:driverClass="com.mysql.jdbc.Driver"
          p:jdbcUrl="jdbc:mysql://localhost/spring"
          p:user="root"
          p:password="123456"
          p:maxPoolSize="40"
          p:minPoolSize="2"
          p:initialPoolSize="2"
          p:maxIdleTime="30"/>
    <!--配置JDBC資料來源的區域性事務管理器,使用DataSourceTransactionManager類-->
    <!--該類實現了PlatformTransactionManager介面,是針對採用資料來源連線的特定實現-->
    <!--配置DataSourceTransactionManager時需要依賴注入DataSource的應用-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
    <!--配置業務邏輯Bean-->
    <bean id="newDao" class="Dao.NewsDao" p:ds-ref="dataSource"/>
    <!--配置事務增強處理Bean,指定事務管理器-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--所有以get開頭的方法是隻讀-->
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="*" isolation="DEFAULT" propagation="REQUIRED" timeout="5"/>
        </tx:attributes>
    </tx:advice>
    <aop:config>
        <!--配置一個切入點,匹配Dao包下的所有方法-->
        <aop:pointcut id="myPoint" expression="execution(* Dao.*.*(..))"/>
        <!--aop:advisor與aop:aspect一樣,用處不一樣,前者用於事務,後者用於切面-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPoint"/>
    </aop:config>
</beans>

<aop:advisor.../>元素是一個很奇怪的東西,標準的AOP機制裡並沒有所謂的Advisor,Advisor是Spring1.x遺留下來的。Advisor的作用非常簡單:將Advice和切入點繫結在一起,保證Advice所包含的增強處理將在對應的切入點被織入。

package Service;

import Dao.NewsDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Main {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        NewsDao dao = applicationContext.getBean("newDao",NewsDao.class);
        dao.insert("燕雙嚶","我賭你的槍裡沒有子彈!");
    }
}

上面程式會產生一個異常,而且insert()方法所執行的兩條SQL語句全部回滾——因為事務控制。

3,使用@Transactional註解配置事務代理

Spring還允許將事務配置放在Java類中定義,這需要藉助於@Transactional註解,該註解既可用於修飾SpringBean類,也可用於修飾Bean類中的方法。

如果使用@Transactional修飾Bean類,則表明這些事務設定對整個Bean類其作用;如果使用@Transactional修飾Bean類的某個方法,則表明這些事務設定只對該方法有效。

使用@Transactional時可指定如下屬性:

  • isolation:用於指定事務的隔離級別。預設為底層事務的隔離級別。
  • noRollbackFor:指定遇到特定異常時強制不回滾事務。
  • noRollbackForClassName:指定遇到特定的多個異常時強制不回滾事務。該屬性可以指定多個異常類名。
  • propagation:指定事務傳播行為。
  • readOnly:指定事務是否只讀。
  • rollbackFor:指定遇到特定異常時強制回滾事務。
  • rollbackForClassName:指定遇到特定的多個異常時強制回滾事務。該屬性值可以指定多個異常類名。
  • timeout:指定事務的超時時長。

其實該註解所指定的屬性與<tx:advice.../>元素中所指定的事務屬性基本上是對應的,它們的意義也基本相似。

package Dao;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.sql.DataSource;
@Aspect
public class NewsDao {
    private DataSource ds;

    public void setDs(DataSource ds) {
        this.ds = ds;
    }
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,timeout = 5)
    public void insert(String title,String content){
        JdbcTemplate jt = new JdbcTemplate(ds);
        jt.update("insert into student values(1,?,?)",title,content);
        jt.update("insert into student values(1,?,?)",title,content);
    }
}
<?xml version="1.0" encoding="GBK"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/cache"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
    <!--定義資料來源Bean,使用C3P0資料來源實現,並注入資料來源的必要資訊-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"
          p:driverClass="com.mysql.jdbc.Driver"
          p:jdbcUrl="jdbc:mysql://localhost/spring"
          p:user="root"
          p:password="123456"
          p:maxPoolSize="40"
          p:minPoolSize="2"
          p:initialPoolSize="2"
          p:maxIdleTime="30"/>
    <!--配置JDBC資料來源的區域性事務管理器,使用DataSourceTransactionManager類-->
    <!--該類實現了PlatformTransactionManager介面,是針對採用資料來源連線的特定實現-->
    <!--配置DataSourceTransactionManager時需要依賴注入DataSource的應用-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
          p:dataSource-ref="dataSource"/>
    <!--配置業務邏輯Bean-->
    <bean id="newDao" class="Dao.NewsDao" p:ds-ref="dataSource"/>
    <!--根據Annotation來生成事務代理-->
    <tx:annotation-driven/>
</beans>