天天看點

Spring筆記——事務

Spring的事務管理不需要與任何特定的事務API耦合。對不同的持久層通路技術,程式設計式事務提供一緻的事務程式設計風格,通過模闆化的操作一緻性的管理事務。聲明式事務基于Spring AOP實作,卻并不需要程式開發者成為AOP專家,亦可輕易使用Spring的聲明式事務管理。

1.Spring支援的事務政策

Java EE應用的傳統事務有兩種政策:全局事務和局部事務,全局事務由應用伺服器管理,需要底層伺服器的JTA支援。局部事務和底層所采用的持久化技術有關,當采用JDBC持久化技術時,需要使用Connection對象來操作事務;而采用Hibernate持久化技術時,需要使用Session對象來操作事務。

全局事務可以跨多個事務性的資源(典型的例子是關系資料庫和消息隊列);使用局部事務,應用伺服器不需要參與事務管理,是以不能保證跨多個事務性資源的事務的正确性。當然,實際上大部分應用都使用單一事務性的資源。

Spring事務政策是通過PlatformTransactionManager接口展現的,該接口是Spring事務政策的核心。該接口的源代碼如下:

public interface PlatformTransactionManager
{
    // 平台無關的獲得事務的方法
    public abstract TransactionStatus getTransaction(TransactionDefinition transactiondefinition)
        throws TransactionException;
    // 平台無關的事務送出方法
    public abstract void commit(TransactionStatus transactionstatus)
        throws TransactionException;
    // 平台無關的事務復原方法
    public abstract void rollback(TransactionStatus transactionstatus)
        throws TransactionException;
}
           

PlatformTransactionManager是一個與任何事務政策分離的接口,随着底層不同僚務政策的切換,應用必須采用不同的實作類。PlatformTransactionManager接口沒有與任何事務資源捆綁在一起,它可以适用于任何的事務政策,結合Spring的IoC容器,可以向PlatformTransactionManager注入相關的平台特性。

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

Spring的事務管理機制是一種典型的政策模式,PlatformTransactionManager代表事務管理接口,但它并不知道底層如何管理事務,它隻要求事務管理需要提供開始事務、送出事務和復原事務三個方法,但具體如何實作則交給其實作類來完成。

在PlatformTransactionManager接口内,包含一個getTransaction(TransactionDefinition transactiondefinition)方法,該方法根據一個TransactionDefinition參數,傳回一個TransactionStatus對象。TransactionStatus 對象表示一個事務,TransactionStatus 被關聯在目前執行的線程上。

getTransaction(TransactionDefinition transactiondefinition)傳回的TransactionStatus對象,可能是一個新的事務,也可能是一個已經存在的事務對象。如果目前執行的線程已經處于事務管理下,則傳回目前線程的事務對象;否則,系統将建立一個事務對象後傳回。

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

☞ 事務隔離:目前事務和其他事務的隔離程度。例如,這個事務能否看到其他事務未送出的資料等。

☞ 事務傳播:通常,在事務中執行的代碼都會在目前事務中運作。但是,如果一個事務上下文已經存在,有幾個選項可指定該事務性方法的執行行為。例如,大多數情況下,簡單地在現有的事務上下文中運作;或者挂起現有事務,建立一個新的事務。

☞ 事務逾時:事務在逾時前能運作多久,也就是事務的最長持續時間。如果事務一直沒有被送出或復原,将在超出該時間後,系統自動復原事務。

☞ 隻讀狀态:隻讀事務不修改任何資料。在某些情況下(例如使用Hibernate時),隻讀事務是非常有用的優化。

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

public interface TransactionStatus
{
    // 判斷事務是否為建立的事務
    boolean isNewTransaction();
    // 設定事務復原
    public abstract void setRollbackOnly();
    // 查詢事務是否已有復原标志
    public abstract boolean isRollbackOnly();
}
           

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

下面提供了不同的持久層通路環境,及其對應的PlatformTransactionManager實作類的配置。

JDBC資料源的局部事務政策的配置檔案如下:

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

    <!-- 定義資料源Bean,使用C3P0資料源實作 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連結資料庫的驅動 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <!-- 指定連結資料庫的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1/javaee" />
        <!-- 指定連結資料庫的使用者名 -->
        <property name="user" value="root" />
        <!-- 指定連結資料庫的密碼 -->
        <property name="password" value="123456" />
        <!-- 指定連結資料庫連接配接池的最大連接配接數 -->
        <property name="maxPoolSize" value="40" />
        <!-- 指定連結資料庫連接配接池的最小連接配接數 -->
        <property name="minPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的初始化連接配接數 -->
        <property name="initialPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的連接配接的最大空閑時間 -->
        <property name="maxIdleTime" value="20" />
    </bean>

    <!-- 配置JDBC資料源的局部事務管理器,使用DataSourceTransactionManager類 -->
    <!-- 該類實作PlatformTransactionManager接口,是針對采用資料源連接配接的特定實作 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager時需要依賴注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
</beans>
           

容器管理的JTA全局事務的配置檔案如下:

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

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <!-- 容器管理資料源的JNDI -->
        <property name="jndiName" value="jdbc/jpetstore" />
    </bean>

    <!-- 使用JtaTransactionManager類,該類實作PlatformTransactionManager接口 -->
    <!-- 針對采用全局事務管理的特定實作 -->
    <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
</beans>
           

從上面的配置檔案來看,當配置JtaTransactionManager全局事務管理政策時,隻需指定事務管理器實作類即可,無須傳入額外的事務資源。這是因為全局事務的JTA資源由Java EE伺服器提供,而Spring容器能自行從Java EE伺服器中擷取該事務資源,是以無須使用依賴注入來配置。

當采用Hibernate持久層通路政策時,局部事務政策的配置檔案如下:

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

    <!-- 定義資料源Bean,使用C3P0資料源實作 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連結資料庫的驅動 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <!-- 指定連結資料庫的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1/javaee" />
        <!-- 指定連結資料庫的使用者名 -->
        <property name="user" value="root" />
        <!-- 指定連結資料庫的密碼 -->
        <property name="password" value="123456" />
        <!-- 指定連結資料庫連接配接池的最大連接配接數 -->
        <property name="maxPoolSize" value="40" />
        <!-- 指定連結資料庫連接配接池的最小連接配接數 -->
        <property name="minPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的初始化連接配接數 -->
        <property name="initialPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的連接配接的最大空閑時間 -->
        <property name="maxIdleTime" value="20" />
    </bean>

    <!-- 定義Hibernate的SessionFactory -->
    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <!-- 配置SessionFactory所需的資料源,注入上文定義的dataSource -->
        <property name="dataSource" ref="dataSource" />
        <!-- mappingResources屬性用來列出全部映射檔案 -->
        <property name="mappingResources">
            <list>
                <!-- 以下用來列出所有的PO映射檔案 -->
                <value>lee/MyTest.hbm.xml</value>
            </list>
        </property>
        <!-- 定義Hibernate的SessionFactory屬性 -->
        <property name="hibernateProperties">
            <props>
                <!-- 指定Hibernate的連接配接方言 -->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>
                <!-- 是否根據Hibernate映射建立資料表 -->
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <!-- 配置Hibernate局部事務管理器,使用HibernateTransactionManager類 -->
    <!-- 該類是PlatformTransactionManager接口針對采用Hibernate的特定實作類 -->
    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
        <!-- 配置HibernateTransactionManager需要依賴注入SessionFactory -->
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
</beans>
           

如果底層采用Hibernate持久層技術,但事務采用JTA全局事務,則Spring配置檔案如下:

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

    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
        <!-- 容器管理資料源的JNDI -->
        <property name="jndiName" value="jdbc/jpetstore" />
    </bean>

    <!-- 定義Hibernate的SessionFactory -->
    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
        <!-- 配置SessionFactory所需的資料源,注入上文定義的dataSource -->
        <property name="dataSource" ref="dataSource" />
        <!-- mappingResources屬性用來列出全部映射檔案 -->
        <property name="mappingResources">
            <list>
                <!-- 以下用來列出所有的PO映射檔案 -->
                <value>lee/MyTest.hbm.xml</value>
            </list>
        </property>
        <!-- 定義Hibernate的SessionFactory屬性 -->
        <property name="hibernateProperties">
            <props>
                <!-- 指定Hibernate的連接配接方言 -->
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</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事務管理政策時,應用程式無須與具體的事務政策耦合。Spring提供了如下兩種事務管理方式:

☞ 程式設計式事務管理:即使利用Spring程式設計式事務時,程式也可直接擷取容器中的transactionManager Bean,該Bean總是PlatformTransactionManager的執行個體,是以可以通過該接口所提供的3個方法來開始事務、送出事務和復原事務。

☞聲明式事務管理:無須在Java程式中書寫任何的事務操作代碼,而是通過在XML檔案中為業務元件配置管理事務代理(AOP代理的一種),AOP為事務代理所織入的增強處理也有Spring提供:在目标方法執行之前,織入開始事務;在目标方法執行之後,織入結束事務。

2.使用TransactionProxyFactoryBean建立事務代理

Spring同時支援程式設計式事務政策和聲明式事務政策,大部分時候,我們都推薦采用聲明式事務政策。使用聲明式事務政策的優勢十分明顯:

☞ 聲明式事務能大大降低開發者的代碼書寫量,而且聲明式事務幾乎不影響應用的代碼。是以,無論底層的事務政策如何變化,應用程式都無須任何改變。

☞ 應用程式代碼無須任何事務處理代碼,可以更專注于業務邏輯的實作。

☞ Spring則可對任何POJO的方法提供事務管理,而且Spring的聲明式事務管理無須容器的支援,可在任何環境下使用。

☞ EJB的CMT無法提供聲明式復原規則:而通過配置檔案,Spring可指定事務在遇到特定異常時自動復原。Spring不僅可在代碼中使用setRollbackOnly復原事務,也可在配置檔案中配置復原規則。

☞ 由于Spring采用AOP的方式管理事務,是以,可以在事務復原動作中插入使用者自己的動作,而不僅僅是執行系統預設的復原。

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

    <!-- 定義資料源Bean,使用C3P0資料源實作 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連結資料庫的驅動 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <!-- 指定連結資料庫的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1/javaee" />
        <!-- 指定連結資料庫的使用者名 -->
        <property name="user" value="root" />
        <!-- 指定連結資料庫的密碼 -->
        <property name="password" value="123456" />
        <!-- 指定連結資料庫連接配接池的最大連接配接數 -->
        <property name="maxPoolSize" value="40" />
        <!-- 指定連結資料庫連接配接池的最小連接配接數 -->
        <property name="minPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的初始化連接配接數 -->
        <property name="initialPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的連接配接的最大空閑時間 -->
        <property name="maxIdleTime" value="20" />
    </bean>

    <!-- 配置JDBC資料源的局部事務管理器,使用DataSourceTransactionManager類 -->
    <!-- 該類實作PlatformTransactionManager接口,是針對采用資料源連接配接的特定實作 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">**
        <!-- 配置DataSourceTransactionManager時需要依賴注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!-- 配置一個業務邏輯Bean -->
    <bean id="newsDao" class="org.crazyit.app.dao.impl.NewsDaoImpl">
        <property name="ds" ref="dataSource"/>
    </bean>

    <!-- 為業務邏輯Bean配置事務代理 -->
    <bean id="newsDaoTrans" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 為事務代理工廠Bean注入事務管理器 -->
        <property name="transactionManager" ref="transactionManager"/>
        <property name="target" ref="newsDao"/>
        <!-- 指定事務屬性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
</beans>
           

上面的配置檔案中第一段粗體字代碼配置了一個事務管理器,該事務管理器是針對JDBC局部事務的特定實作類。程式第二段粗體字代碼為test Bean配置了事務代理。

配置事務代理時需要傳入一個事務管理器、一個目标Bean,并指定該事物代理的事務屬性,事務屬性由transactionManager屬性指定。上面事務屬性隻有一條事務傳播規則,該規則指定對于所有方法都使用PROPAGATION_REQUIRED的傳播規則。Spring支援的事務傳播規則如下:

☞ PROPAGATION_MANDATORY:要求調用該方法的線程必須處于事務環境中,否則抛出異常

☞ PROPAGATION_NESTED:如果執行該方法的線程已處于事務環境下,依然啟動新的事務,方法在嵌套的事務裡執行。如果執行該方法的線程并未處于事務中,也啟動新的事務,然後執行該方法,此時與PROPAGATION_REQUIRED相同

☞ PROPAGATION_NEVER:不允許調用該方法的線程處于事務環境系下,如果調用該方法的線程處于事務環境下,則抛出異常

☞ PROPAGATION_NOT_SUPPORTED:如果調用該方法的線程處于事務中,則先暫停目前事務,然後執行該方法

☞ PROPAGATION_REQUIRED:要求在事務環境中執行該方法,如果目前執行線程已處于事務中,則直接調用;如果目前執行線程不處于事務中,則啟動新的事務後執行該方法

☞ PROPAGATION_REQUIRED_NEW:該方法要求在新的事務環境中執行,如果目前執行線程已處于事務中,則先暫停目前事務,啟動新事務後執行該方法;如果目前調用線程不處于事務中,則啟動新的事務後執行方法

☞ PROPAGATION_SUPPORTS:如果目前執行線程處于事務中,則使用目前事務,否則不使用事務

事實上,Spring不僅支援對接口的代理,整合CGLIB後,Spring甚至可以對具體類生成代理,隻要設定proxy-target-class屬性為true就可以。如果目标Bean沒有實作任何接口,proxy-target-class屬性預設被設為true,此時Spring會對具體類生成代理。

<!-- 表示使用cglib,而非JDK的動态代理,因為Controller沒有實作接口,是以要配置這裡 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
           

3.Spring2.X的事務配置政策

雖然前面介紹的TransactionProxyFactoryBean配置政策簡單易懂,但配置起來極為繁瑣:每個目标Bean都需要額外配置一個TransactionProxyFactoryBean代理,這種方式将導緻配置檔案急劇增加。

Spring2.X的XML Schema方式提供了更簡潔的事務配置政策,Spring2.X提供了tx命名空間來配置事務管理,tx命名空間下提供了

<tx:advice.../>

元素來配置事務增強處理,一旦使用該元素配置了事務增強處理,就可以直接使用

<aop:advisor.../>

為容器中一批Bean配置自動事務代理。

<?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:mvc="http://www.springframework.org/schema/mvc"
    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/context
        http://www.springframework.org/schema/context/spring-context-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">

    <!-- 定義資料源Bean,使用C3P0資料源實作 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定連結資料庫的驅動 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <!-- 指定連結資料庫的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1/javaee" />
        <!-- 指定連結資料庫的使用者名 -->
        <property name="user" value="root" />
        <!-- 指定連結資料庫的密碼 -->
        <property name="password" value="123456" />
        <!-- 指定連結資料庫連接配接池的最大連接配接數 -->
        <property name="maxPoolSize" value="40" />
        <!-- 指定連結資料庫連接配接池的最小連接配接數 -->
        <property name="minPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的初始化連接配接數 -->
        <property name="initialPoolSize" value="1" />
        <!-- 指定連結資料庫連接配接池的連接配接的最大空閑時間 -->
        <property name="maxIdleTime" value="20" />
    </bean>

    <!-- 配置JDBC資料源的局部事務管理器,使用DataSourceTransactionManager類 -->
    <!-- 該類實作PlatformTransactionManager接口,是針對采用資料源連接配接的特定實作 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager時需要依賴注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource" />
    </bean>

     <!-- 配置一個業務邏輯Bean -->
    <bean id="newsDao" class="org.crazyit.app.dao.impl.NewsDaoImpl">
        <property name="ds" ref="dataSource"/>
    </bean>

    <!-- 配置事務增強處理Bean,指定事務管理器 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 用于配置詳細的事務語義 -->
        <tx:attributes>
            <!-- 所有以'get'開頭的方法時read-only的 -->
            <tx:method name="get*" read-only="true"/>
            <!-- 其他方法使用預設的事務設定 -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

    <!-- AOP配置的元素 -->
    <aop:config>
        <!-- 配置一個切入點,比對 org.crazyit.app.dao.iml包下所有以Impl結尾的類裡、所有方法的執行 -->
        <aop:pointcut id="myPointcut" expression="execution(* org.crazyit.app.dao.iml.*Impl.*(..))"/>
        <!-- 指定在txAdvice切入點應用txAdvice事務增強處理 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="myPointcut"/>
    </aop:config>
</beans>
           

上面的配置檔案的第一段粗體字代碼使用XML Schema啟用了Spring配置檔案的tx、aop兩個命名空間,配置檔案的第二段粗體字代碼配置了一個事務增強處理,配置

<tx:advice.../>

元素時隻需指定一個transaction-manager屬性,該屬性的預設值是‘transactionManager’

配置檔案中最後一段粗體字是

<aop:config.../>

定義,它確定由txAdvice切面定義事務增強處理能在合适的點被織入。首先我們定義了一個切入點,它比對org.crazyit.app.dao.iml包下所有以Impl結尾的類所包含的所有方法,我們把該切入點叫做myPointcut。然後用一個Advisor把這個切入點與txAdvice綁定在一起,表示當myPointcut執行時,txAdvice定義的增強處理将被織入。

當我們使用這種配置政策時,無須為每個業務Bean專門配置事務代理,Spring AOP會為業務元件自動生成代理,程式可以直接請求容器中test Bean,該Bean的方法已經具有了事務性——因為該Bean的實作類位于org.crazyit.app.dao.iml包下,且以Impl結尾,和myPointcut切入點比對。

配置

<method.../>

子元素可以指定如下幾個屬性:

☞ name:必選屬性,與該事務語義關聯的方法名。該屬性支援視同通配符

☞ propagation:指定事務傳播行為,該屬性值可為Propagation枚舉類的任一枚舉值。該屬性的預設值為Propagation.REQUIRED

☞ isolation:指定事務隔離級别,該屬性值可為Isolation枚舉類的任一枚舉值,該屬性的預設值為Isolation.DEFAULT

☞ timeout:指定事務逾時的時間(以秒為機關),指定-1意味着不逾時,該屬性的預設值是-1

☞ read-only:指定事務是否隻讀。該屬性的預設值是false

☞ rollback-for:指定出發事務復原的異常類(應使用全限定類名),該屬性可指定多個異常類,多個異常類之間以英文逗号隔開

☞ no-rollback-for:指定不觸發事務復原的異常類(應使用全限定類名),該屬性可指定多個異常類,多個異常類之間以英文逗号隔開

提示:在預設情況下,隻有當抛出運作時異常和unChecked異常時,Spring事務架構才會自動復原事務。也就是說,隻有當抛出一個RuntimeException或其子類執行個體,以及Error對象,Spring才會自動復原事務。如果事務方法中抛出Checked異常,則事務不會自動復原。

通過使用rollback-for屬性可強制Spring遇到特定Checked異常時自動復原事務,下面的XML配置片段示範了這種用法。

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- 所有以get開頭的方法是隻讀的,且當事務方法抛出NoItemException異常時復原 -->
        <tx:method name="get*" read-only="true" rollback-for="exception.NoItemException"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
           

如果想讓事務方法抛出指定異常時強制不復原事務,則可通過no-rollback-for屬性來指定,如下面的配置片段所示。

<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <!-- 所有以get開頭的方法是隻讀的,且當事務方法抛出NoItemException異常時不復原 -->
        <tx:method name="get*" read-only="true" no-rollback-for="exception.NoItemException"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
           

4.使用@Transactional

Spring還允許将事務配置在設定在Java類定義中,這需要借助于@Transactional,該注解既可用于修飾Spring Bean類,也可用于修飾Bean類中的某個方法。

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

使用@Transactional時可指定如下方法。

☞ isolation:用于指定事務隔離的級别。預設為底層事務的隔離級别

☞ noRollbackFor:指定遇到指定異常時強制不復原事務

☞ noRollbackForClassName:指定遇到指定多個異常時強制不復原事務。該屬性值可以指定多個異常類名

☞ propagation:指定事務傳播屬性

☞ readOnly:指定事務是否隻讀

☞ rollbackFor:指定遇到指定異常時強制復原事務

☞ rollbackForClassName:指定遇到指定多個異常時強制復原事務。該屬性值可以指定多個異常類名

☞ timeout:指定事務的逾時時長

根據上面的解釋不難看出,其實該Annotation所指定的屬性與

<tx:advice.../>

元素中能指定的事務屬性基本上是對應的,他們的意義也基本相似。

下面使用@Transactional修飾需要添加事務的方法

public class NewsDaoImpl implements NewsDao {

    @Transactional(propagation = Propagation.REQUIRED)
    @Override
    public void insert(String title, String content) {
        .........
    }
}
           

上面的Bean類中insert()方法使用了@Transactional修飾,表明該方法就會具有事務性。僅使用這個Annotation修飾還不夠,還需要讓Spring根據Annotation來配置事務代理。是以還需要在Spring配置檔案中增加如下配置片段:

<!-- 配置JDBC資料源的局部事務管理器,使用DataSourceTransactionManager類 -->
<!-- 該類實作PlatformTransactionManager接口,是針對采用資料源連接配接的特定實作 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 配置DataSourceTransactionManager時需要依賴注入DataSource的引用 -->
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 根據Annotation來生成事務代理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>