Spring事務——使用TransactionProxyFactoryBean建立事務代理

    Spring同時支持編程式事務策略和聲明式事務策略,大部分時候,咱們都推薦採用聲明式事務策略。使用聲明式事務策略的優點十分明顯:
java

  • 聲明式事務能大大下降開發者的代碼書寫量,並且聲明式事務幾乎不影響應用的代碼。所以,不論底層事務策略如何變化,應用程序都無需任何改變mysql

  • 應用程序代碼無需任何事務處理代碼,能夠更專一於業務邏輯的實現spring

  • Spring可對任何POJO的方法提供事務管理,並且Spring的聲明式事務管理無需容器的支持,可在任何環境下使用sql

  • EJB的CMT沒法提供聲明式回滾規則;而經過配置文件,Spring可指定事務在遇到特定異常時自動回滾。Spring不只可在代碼中使用setRollbackOnly回滾事務,也可在配置文件中配置回滾規則數據庫

  • 因爲Spring採用AOP的方式管理事務,所以,能夠在事務回滾動做中插入用戶本身的動做,而不只僅是執行系統默認的回滾編程

    本文主要介紹Spring中聲明式事務管理的使用。測試

    在Spring1.X中,聲明式事務使用TransactionProxyFactoryBean來配置事務代理Bean。正如它的類名所暗示的,它是一個專門爲目標Bean生成事務代理的工廠Bean。既然TransactionProxyFactoryBean產生的是事務代理Bean,可見Spring的聲明式事務策略是基於Spring AOP的。this

    每一個TransactionProxyFactoryBean爲一個目標Bean生成一個事務代理Bean,事務代理的方法改寫了目標Bean的方法,就是在目標Bean的方法執行以前加入開始事務,在目標Bean的方法正常結束以前提交事務,若是遇到特定異常則回滾。spa

    TransactionProxyFactoryBean建立事務代理時,須要瞭解當前事務所處的環境,該環境屬性經過PlatformTransactionManager實例(其實現類的實例)傳入,而相關事務規則則在該Bean定義中給出。下面是一個簡單的持久化測試程序,該程序插入兩條數據,這兩條數據徹底相同,將違反惟一鍵約束:線程

package com.abc.dao.impl;

public class NewsDaoImpl implements NewsDao {
    private DataSource dataSource;
    public void setDataSource(DataSrouce dataSource) {
        this.dataSource = dataSource;
    }
    public void insert(String title, String content) {
        JdbcTemplate template = new JdbcTemplate(dataSource);
        template.update("insert into news_table values (....)");
        //兩次相同的操做,將違反主鍵約束
        template.update("insert into news_table values (....)");
    }
}

    上面的程序中,兩次update語句將會違反主鍵約束——該行代碼將會引起異常,若是在沒有事務的環境下,前一條代碼會向數據庫中插入一條記錄;但若是在增長了事務控制的環境下,則這兩條語句是一個總體,由於第二條語句插入失敗將致使第一條插入的記錄也被回滾。下面是在Spring配置文件中配置該測試程序,並使用TransactionProxyFactoryBean爲它們配置事務代理:

<!-- 使用C3P0數據庫鏈接池做爲數據源 -->
<bean id="dataSource" 
    class="com.mchange.v2.c3p0.ComboPolledDataSource" destroy-method="close">
    <property name="driverClass" value="com.mysql.jdbc.Driver" />
    <property name="jdbcUrl" value="jdbc:mysql://localhost/test" />
    <property name="user" value="root" />
    <property name="password" value="root" />
    <property name="maxPoolSize" value="40" />
    <property name="minPoolSize" value="4" />
    <property name="initialPoolSize" value="10" />
    <!-- 指定數據庫鏈接池的鏈接的最大空閒時間 -->
    <property name="maxIdleTime" value="20" />
</bean>

<!-- 配置JDBC數據源的局部事務管理器,使用DataSourceTransactionManager類,該類實現了
PlatformTransactionManager接口,是針對採用數據源鏈接的特定實現 -->
<bean id="transactionManager" 
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 配置TransactionManager時須要注入數據源引用 -->
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- 下面這個是前面定義的業務Bean -->
<bean id="newsDao" class="com.abc.dao.impl.NewsDaoImpl">
    <!-- 爲業務Bean注入屬性 -->
    <property name="dataSource" ref="dataSource" />
</bean>

<bean id="newsDaoTransProxy" 
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <!-- 爲事務代理工廠Bean注入事務管理器 -->
    <property name="transactionManager" ref="transactionManager" />
    <!-- 要在哪一個Bean上面建立事務代理對象 -->
    <property name="target" ref="newsDao" />
    <!-- 指定事務屬性 -->
    <property name="transactionAttributes">
        <props>
            <prop key="*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

    上面的配置文件中定義了一個事務管理器transactionManager,該事務管理器是針對JDBC局部事務的特定實現類。配置事務代理(如上面的newsDaoTransProxy)時須要傳入一個事務管理器,一個目標Bean,並指定該事務代理的事務屬性。事務屬性由transactionAttributes屬性指定。上面事務屬性只有一條事務傳播規則,該規則制定對於全部方法都使用PROPAGATION_REQUIRED的傳播規則。Spring支持的事務傳播規則以下:

  • PROPAGATION_MANDATORY:要求調用該方法的線程必須處於事務環境中,不然拋出異常

  • PROPAGATION_NESTED:若是執行該方法的線程已處於事務環境下,依然啓動新的事務,方法在嵌套的事務裏執行。若是執行方法的線程爲處於事務中,也啓動新的事務,而後執行該方法,此時與PROPAGATION_REQUIRED相同

  • PROPAGATION_NEVER:不容許調用該方法的線程處於事務環境下,若是調用該方法的線程處於事務環境下,則拋出異常

  • PROPAGATION_NOT_SUPPORTED:若是調用該方法的線程處在事務中,則暫停當前事務,而後執行該方法

  • PROPAGATION_REQUIRED:要求在事務環境中執行該方法,若是當前執行的線程已處於事務中,則直接調用;若是當前執行線程不處於事務中,則啓動新的事務後執行該方法

  • PROPAGATION_REQUIRES_NEW:要求在事務環境中執行該方法,若是當前執行的線程已處於事務中,則暫停當前事務,啓動新事務後執行該方法;若是當前執行線程不處於事務中,則啓動新的事務後執行該方法

  • PROPAGATION_SUPPORTS:若是當前執行線程處於事務中,則使用當前事務;不過不在事務中,則不使用事務

    主程序中主要獲取了定義的NewsDao類型的Bean,並調用其insert方法,下面是主程序:

public class SpringTest {
    public static void main(String[] arg) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        NewsDao dao = (NewsDao)context.getBean("newsDaoTransProxy",NewsDao.class);
        dao.insert("新聞標題","新聞內容");
    }
}

    上面的第4行中獲取了newsDaoTransProxy Bean,該Bean已經不在是NewsDaoImpl類的實例了,它只是Spring容器建立的事務代理,該事務代理以NewsDaoImpl實例爲目標對象,且該目標對象也實現了NewsDao接口(與NewsDaoImpl實現了相同的接口),故代理對象也能夠當成NewsDao實例來使用。運行上面的程序,將出現一個異常,並且insert方法所執行的兩條SQL語句所有回滾——由於事務控制的緣故。

    當咱們使用TransactionProxyFactoryBean爲目標Bean配置了事務代理之後,SpringAOP將會把負責事務操做的加強處理織入目標Bean的業務方法當中。事實上,Spring不只支持對接口的代理,整合CGLIB後,Spring甚至能夠對具體類生成代理,只要設置proxyTargetClass屬性爲true便可。若是目標Bean沒有實現任何接口,proxyTargetClass屬性默認被設爲true,此時Spring會對具體類生成代理。固然一般建議面向接口編程,而不要面向具體的實現類編程。

相關文章
相關標籤/搜索