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會對具體類生成代理。固然一般建議面向接口編程,而不要面向具體的實現類編程。