標籤:sql 需求 兩種 異常類 代理 相同屬性 xpl 下載 建立
概述交易管理對於公司專屬應用程式來說是至關重要的,即使出現異常情況,它也可以保證資料的一致性。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設定檔,另一種就是基於@Transactional註解。顯然基於註解的方式更簡單易用,更清爽。
自動認可(AutoCommit)與串連關閉時的是否自動認可
自動認可
預設情況下,資料庫處於自動認可模式。每一條語句處於一個單獨的事務中,在這條語句執行完畢時,如果執行成功則隱式的提交事務,如果
執行失敗則隱式的復原事務。
對於正常的交易管理,是一組相關的操作處於一個事務之中,因此必須關閉資料庫的自動認可模式。不過,這個我們不用擔心,spring會將底層串連的自動認可特性設定為false。
org/springframework/jdbc/datasource/DataSourceTransactionManager.java
// switch to manual commit if necessary. this is very expensive in some jdbc drivers,// so we don‘t want to do it unnecessarily (for example if we‘ve explicitly// configured the connection pool to set it already).if (con.getautocommit()) { txobject.setmustrestoreautocommit(true); if (logger.isdebugenabled()) { logger.debug("switching jdbc connection [" + con + "] to manual commit"); } con.setautocommit(false);}
有些資料連線池提供了關閉事務自動認可的設定,最好在設定串連池時就將其關閉。但C3P0沒有提供這一特性,只能依靠spring來設定。
因為JDBC規範規定,當連線物件建立時應該處於自動認可模式,這是跨DBMS的預設值,如果需要,必須顯式的關閉自動認可。C3P0遵守這一規範,讓客戶代碼來顯式的設定需要的提交模式。
串連關閉時的是否自動認可
當一個串連關閉時,如果有未提交的事務應該如何處理?JDBC規範沒有提及,C3P0預設的策略是復原任何未提交的事務。這是一個正確的策略,但JDBC驅動供應商之間對此問題並沒有達成一致。
C3P0的autoCommitOnClose屬性預設是false,沒有十分必要不要動它。或者可以顯式的設定此屬性為false,這樣會更明確。
基於註解的聲明式交易管理配置
spring-servlet.xml
<!-- transaction support--><!-- PlatformTransactionMnager --><bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /></bean><!-- enable transaction annotation support --><tx:annotation-driven transaction-manager="txManager" />
還要在spring-servlet.xml中添加tx名字空間
... xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" ... http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd ...
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 { TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) 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屬性
屬性 |
類型 |
描述 |
value |
String |
可選的限定描述符,指定使用的交易管理員 |
propagation |
enum: Propagation |
可選的事務傳播行為設定 |
isolation |
enum: Isolation |
可選的交易隔離等級設定 |
readOnly |
boolean |
讀寫或唯讀事務,預設讀寫 |
timeout |
int (in seconds granularity) |
事務逾時時間設定 |
rollbackFor |
Class對象數組,必須繼承自Throwable |
導致交易回復的異常類數組 |
rollbackForClassName |
類名數組,必須繼承自Throwable |
導致交易回復的異常類名字數組 |
noRollbackFor |
Class對象數組,必須繼承自Throwable |
不會導致交易回復的異常類數組 |
noRollbackForClassName |
類名數組,必須繼承自Throwable |
不會導致交易回復的異常類名字數組 |
用法
@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 }}
spring,mybatis交易管理配置與@Transactional註解使用[轉]