概述
事務管理對於企業應用來說是至關重要的,即使出現異常情況,它也可以保證數據的一致性。Spring Framework
對事務管理提供了一致的抽象,其特點如下:
-
為不同的事務API提供一致的編程模型,比如JTA(
Java Transaction API
), JDBC, Hibernate, JPA(Java Persistence API
)和JDO(Java Data Objects
) -
支持聲明式事務管理,特別是基於注解的聲明式事務管理,簡單易用
-
提供比其他事務API如JTA更簡單的編程式事務管理API
-
與spring數據訪問抽象的完美集成
事務管理方式
spring支持編程式事務管理和聲明式事務管理兩種方式。
編程式事務管理使用TransactionTemplate
或者直接使用底層的PlatformTransactionManager
。對於編程式事務管理,spring推薦使用TransactionTemplate
。
聲明式事務管理建立在AOP之上的。其本質是對方法前后進行攔截,然后在目標方法開始之前創建或者加入一個事務,在執行完目標方法之后根據執行情況提交或者回滾事務。聲明式事務最大的優點就是不需要通過編程的方式管理事務,這樣就不需要在業務邏輯代碼中摻雜事務管理的代碼,只需在配置文件中做相關的事務規則聲明(或通過基於@Transactional
注解的方式),便可以將事務規則應用到業務邏輯中。
顯然聲明式事務管理要優於編程式事務管理,這正是spring倡導的非侵入式的開發方式。聲明式事務管理使業務代碼不受污染,一個普通的POJO對象,只要加上注解就可以獲得完全的事務支持。和編程式事務相比,聲明式事務唯一不足地方是,后者的最細粒度只能作用到方法級別,無法做到像編程式事務那樣可以作用到代碼塊級別。但是即便有這樣的需求,也存在很多變通的方法,比如,可以將需要進行事務管理的代碼塊獨立為方法等等。
聲明式事務管理也有兩種常用的方式,一種是基於tx和aop名字空間的xml配置文件,另一種就是基於@Transactiona
l注解。顯然基於注解的方式更簡單易用,更清爽。
自動提交(AutoCommit)與連接關閉時的是否自動提交
自動提交
默認情況下,數據庫處於自動提交模式。每一條語句處於一個單獨的事務中,在這條語句執行完畢時,如果執行成功則隱式的提交事務,如果執行失敗則隱式的回滾事務。
對於正常的事務管理,是一組相關的操作處於一個事務之中,因此必須關閉數據庫的自動提交模式。不過,這個我們不用擔心,spring會將底層連接的自動提交特性設置為false。
有些數據連接池提供了關閉事務自動提交的設置,最好在設置連接池時就將其關閉。但C3P0沒有提供這一特性,只能依靠spring來設置。
因為JDBC規范規定,當連接對象建立時應該處於自動提交模式,這是跨DBMS的缺省值,如果需要,必須顯式的關閉自動提交。C3P0遵守這一規范,讓客戶代碼來顯式的設置需要的提交模式。
連接關閉時的是否自動提交
當一個連接關閉時,如果有未提交的事務應該如何處理?JDBC規范沒有提及,C3P0默認的策略是回滾任何未提交的事務。這是一個正確的策略,但JDBC驅動提供商之間對此問題並沒有達成一致。
C3P0的autoCommitOnClose
屬性默認是false,沒有十分必要不要動它。或者可以顯式的設置此屬性為false,這樣會更明確。
基於注解的聲明式事務管理配置
<!--事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mobileDS" />
<property name="globalRollbackOnParticipationFailure" value="false" />
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="change*" propagation="REQUIRED" />
<tx:method name="create*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:advisor pointcut="execution(* com.****.*.service..*Service.*(..))" advice-ref="txAdvice" order="1" />
</aop:config>
MyBatis自動參與到spring事務管理中,無需額外配置,只要org.mybatis.spring.SqlSessionFactoryBean
引用的數據源與DataSourceTransactionManager
引用的數據源一致即可,否則事務管理會不起作用。
另外需要下載依賴包aopalliance.jar
放置到WEB-INF/lib
目錄下。否則spring初始化時會報異常java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor
spring事務特性
spring所有的事務管理策略類都繼承自org.springframework.transaction.PlatformTransactionManager
接口
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; }
其中TransactionDefinition
接口定義以下特性:
事務隔離級別
隔離級別是指若干個並發的事務之間的隔離程度。TransactionDefinition 接口中定義了五個表示隔離級別的常量:
-
TransactionDefinition.ISOLATION_DEFAULT
:這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED
。 -
TransactionDefinition.ISOLATION_READ_UNCOMMITTED
:該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀,不可重復讀和幻讀,因此很少使用該隔離級別。比如PostgreSQL
實際上並沒有此級別。 -
TransactionDefinition.ISOLATION_READ_COMMITTED
:該隔離級別表示一個事務只能讀取另一個事務已經提交的數據。該級別可以防止臟讀,這也是大多數情況下的推薦值。 -
TransactionDefinition.ISOLATION_REPEATABLE_READ
:該隔離級別表示一個事務在整個過程中可以多次重復執行某個查詢,並且每次返回的記錄都相同。該級別可以防止臟讀和不可重復讀。 -
TransactionDefinition.ISOLATION_SERIALIZABLE
:所有的事務依次逐個執行,這樣事務之間就完全不可能產生干擾,也就是說,該級別可以防止臟讀、不可重復讀以及幻讀。但是這將嚴重影響程序的性能。通常情況下也不會用到該級別。
事務傳播行為
所謂事務的傳播行為是指,如果在開始當前事務之前,一個事務上下文已經存在,此時有若干選項可以指定一個事務性方法的執行行為。在TransactionDefinition
定義中包括了如下幾個表示傳播行為的常量:
-
TransactionDefinition.PROPAGATION_REQUIRED
:如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。這是默認值。 -
TransactionDefinition.PROPAGATION_REQUIRES_NEW
:創建一個新的事務,如果當前存在事務,則把當前事務掛起。 -
TransactionDefinition.PROPAGATION_SUPPORTS
:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。 -
TransactionDefinition.PROPAGATION_NOT_SUPPORTED
:以非事務方式運行,如果當前存在事務,則把當前事務掛起。 -
TransactionDefinition.PROPAGATION_NEVER
:以非事務方式運行,如果當前存在事務,則拋出異常。 -
TransactionDefinition.PROPAGATION_MANDATORY
:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。 -
TransactionDefinition.PROPAGATION_NESTED
:如果當前存在事務,則創建一個事務作為當前事務的嵌套事務來運行;如果當前沒有事務,則該取值等價於TransactionDefinition.PROPAGATION_REQUIRED
。
事務超時
所謂事務超時,就是指一個事務所允許執行的最長時間,如果超過該時間限制但事務還沒有完成,則自動回滾事務。在 TransactionDefinition
中以 int
的值來表示超時時間,其單位是秒。
默認設置為底層事務系統的超時值,如果底層數據庫事務系統沒有設置超時值,那么就是none,沒有超時限制。
事務只讀屬性
只讀事務用於客戶代碼只讀但不修改數據的情形,只讀事務用於特定情景下的優化,比如使用Hibernate的時候。
默認為讀寫事務。
spring事務回滾規則
指示spring事務管理器回滾一個事務的推薦方法是在當前事務的上下文內拋出異常。spring事務管理器會捕捉任何未處理的異常,然后依據規則決定是否回滾拋出異常的事務。
默認配置下,spring只有在拋出的異常為運行時unchecked異常時才回滾該事務,也就是拋出的異常為RuntimeException
的子類(Errors也會導致事務回滾),而拋出checked異常則不會導致事務回滾。
可以明確的配置在拋出那些異常時回滾事務,包括checked異常。也可以明確定義那些異常拋出時不回滾事務。
還可以編程性的通過setRollbackOnly()
方法來指示一個事務必須回滾,在調用完setRollbackOnly()
后你所能執行的唯一操作就是回滾。
@Transactional注解
@Transactional屬性
用法
@Transactional
可以作用於接口、接口方法、類以及類方法上。當作用於類上時,該類的所有 public
方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該標注來覆蓋類級別的定義。
雖然 @Transactional
注解可以作用於接口、接口方法、類以及類方法上,但是 Spring 建議不要在接口或者接口方法上使用該注解,因為這只有在使用基於接口的代理時它才會生效。另外, @Transactional
注解應該只被應用到 public 方法上,這是由 Spring AOP
的本質決定的。如果你在 protected
、private
或者默認可見性的方法上使用 @Transactional
注解,這將被忽略,也不會拋出任何異常。
默認情況下,只有來自外部的方法調用才會被AOP代理捕獲,也就是,類內部方法調用本類內部的其他方法並不會引起事務行為,即使被調用方法使用@Transactional
注解進行修飾。
@Transactional(readOnly = true) public class DefaultFooService implements FooService { public Foo getFoo(String fooName) { // do something
} // these settings have precedence for this method //方法上注解屬性會覆蓋類注解上的相同屬性
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW) public void updateFoo(Foo foo) { // do something
} }