入口(了解一些基本概念)
Spring事務屬性(事務的屬性有哪些?)
我們都知道事務有開始,保存點,提交,回滾,隔離級別等屬性。那么Spring對于事務屬性定義有哪些呢?通過TransactionDefinition接口我們可以了解到:
1
2
3
4
5
6
|
public interface TransactionDefinition{ int getIsolationLevel(); int getPropagationBehavior(); int getTimeout(); boolean isReadOnly(); } |
獲取隔離級別
獲取傳播特性
獲取超時
獲取是否只讀
事務隔離級別
隔離離別也是通過TransactionDefinition接口定義的,代表并發事務的隔離程度。
隔離級別 | 描述 |
---|---|
TransactionDefinition.ISOLATION_DEFAULT | 這是默認值,表示使用底層數據庫的默認隔離級別。對大部分數據庫而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED |
TransactionDefinition.ISOLATION_READ_UNCOMMITTED | 該隔離級別表示一個事務可以讀取另一個事務修改但還沒有提交的數據。該級別不能防止臟讀和不可重復讀,因此很少使用該隔離級別 |
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 |
* 事務超時
指定事務的最大運行時間。使用int指定,單位是秒。
* 事務的只讀屬性
事務的只讀屬性是指,對事務性資源進行只讀操作或者是讀寫操作。所謂事務性資源就是指那些被事務管理的資源,比如數據源、 JMS 資源,以及自定義的事務性資源等等。如果確定只對事務性資源進行只讀操作,那么我們可以將事務標志為只讀的,以提高事務處理的性能。在 TransactionDefinition 中以 boolean 類型來表示該事務是否只讀。
* 事務的回滾規則
默認出現RuntimeException就會回滾。如果沒有拋出任何異常,或者拋出了已檢查異常,則仍然提交事務。這通常也是大多數開發者希望的處理方式,也是 EJB 中的默認處理方式。(這里個人理解的是已檢查異常,是我們定義的checkedException,包括我們自定義的異常和調用方法捕獲的異常)
Spring事務的三個基本類
Spring 框架中,涉及到事務管理的 API 大約有100個左右,其中最重要的有三個:TransactionDefinition、PlatformTransactionManager、TransactionStatus。所謂事務管理,其實就是“按照給定的事務規則來執行提交或者回滾操作”。“給定的事務規則”就是用 TransactionDefinition 表示的,“按照……來執行提交或者回滾操作”便是用 PlatformTransactionManager 來表示,而 TransactionStatus 用于表示一個運行著的事務的狀態。打一個不恰當的比喻,TransactionDefinition 與 TransactionStatus 的關系就像程序和進程的關系。
TransactionDefinition 定義事務的屬性
TransactionStatus 定義事務的狀態
1
2
3
4
5
|
public interface TransactionStatus{ boolean isNewTransaction(); void setRollbackOnly(); boolean isRollbackOnly(); } |
PlatformTransactionManager 就是各種事務平臺的實現接口
1
2
3
4
5
6
|
Public interface PlatformTransactionManager{ TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; void commit(TransactionStatus status) throws TransactionException; void rollback(TransactionStatus status) throws TransactionException; } |
所以我們現在可以向下,spring的事務真正實現是PlatformTransactionManager的實現類,通過各種參數來符合TransactionDefinition和TransactionStatus的要求,最后根據我們編程的或者聲明的進行事務管理。
根據底層框架的不同(稍后我們看一下DataSourceTransactoinManager和HibernateTransactionManager的代碼,主要做了什么),Spring(或者其他框架)提供主要的實現如下:
DataSourceTransactionManager: 適合JDBC和ibatis
HibernateTransactionManager: 適合hibernate
JpaTransactionManager: 適用于使用JPA進行數據持久化操作的情況(更底層的一些)
適用于使用JPA進行數據持久化操作的情況
到這里基本概念終于結束了,我們可以介紹兩種類型的事務管理了
編程式事務管理
首先我們回想一下不適用spring事務管理時,hibernate事務的管理是怎么樣的? 大概是我們手動獲取session,獲取transaction,開始transaction,提交或者回滾,關閉session
那么我們使用spring的管理之后,事務本身控制還是交給持久框架自己管理。知識spring像一個代理人一樣,你告訴它,它之后轉化后告訴底層框架。
看個實際例子吧:
基于底層API的編程式事務管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionDefinition txDefinition; // transaction定義是哪個 private PlatformTransactionManager txManager; //具體使用的txmanager ...... public boolean transfer(Long fromId, Long toId, double amount) { //這里獲取事務狀態 TransactionStatus txStatus = txManager.getTransaction(txDefinition); boolean result = false ; try { result = bankDao.transfer(fromId, toId, amount); //提交事務 txManager.commit(txStatus); } catch (Exception e) { result = false ; //回滾 txManager.rollback(txStatus); System.out.println( "Transfer Error!" ); } return result; } } |
對應的xml文件:
1
2
3
4
5
6
7
8
9
|
<bean id= "bankService" class = "footmark.spring.core.tx.programmatic.origin.BankServiceImpl" > <property name= "bankDao" ref= "bankDao" /> <property name= "txManager" ref= "transactionManager" /> <property name= "txDefinition" > <bean class = "org.springframework.transaction.support.DefaultTransactionDefinition" > <property name= "propagationBehaviorName" value= "PROPAGATION_REQUIRED" /> </bean> </property> </bean> |
但是這種寫法和我們不適用spring的有何不同呢,最多是將事務層次提高了service層,不限于dao層,所以Spring做了改進:
基于TransactionTemplate的編程式事務管理
TransactionTemplate的execute方法提供一個內部匿名類,用來我們寫transaction代碼,然后提供一個transactionStatus的參數,這樣你可以控制回滾。這樣一來,我們就不用寫任何關于事務API的代碼了。格式大概是 Boolean b = transactionTempate.execute(new TransactionCallBack() { 執行方法(TransactionStatus transactionStatus){} },當執行完成后返回一個boolean的值. 還有一個方法,就是不提供返回結果的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public class BankServiceImpl implements BankService { private BankDao bankDao; private TransactionTemplate transactionTemplate; ...... public boolean transfer( final Long fromId, final Long toId, final double amount) { //調用一個回調函數 return (Boolean) transactionTemplate.execute( new TransactionCallback(){ public Object doInTransaction(TransactionStatus status) { Object result; try { result = bankDao.transfer(fromId, toId, amount); } catch (Exception e) { status.setRollbackOnly(); result = false ; System.out.println( "Transfer Error!" ); } return result; } }); } } |
對應的XML:
1
2
3
4
5
|
< bean id = "bankService" class = "footmark.spring.core.tx.programmatic.template.BankServiceImpl" > < property name = "bankDao" ref = "bankDao" /> < property name = "transactionTemplate" ref = "transactionTemplate" /> </ bean > |
從結果來看,好像還是不夠簡單和清晰。下面我們來看聲明式事務管理,也是比較推薦的方式
聲明式事務管理
Spring的聲明式事務管理是基于AOP的,在方法前和后加上切點,用來打開事務和提交/回滾事務。
基于TransactionInterceptor的管理
最初,Spring 提供了 TransactionInterceptor 類來實施聲明式事務管理功能
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
<beans...> ...... <bean id= "transactionInterceptor" class = "org.springframework.transaction.interceptor.TransactionInterceptor" > <property name= "transactionManager" ref= "transactionManager" /> <property name= "transactionAttributes" > <props> //指定了方法,可以使用通配符 <prop key= "transfer" >PROPAGATION_REQUIRED</prop> </props> </property> </bean> <bean id= "bankServiceTarget" class = "footmark.spring.core.tx.declare.origin.BankServiceImpl" > <property name= "bankDao" ref= "bankDao" /> </bean> <bean id= "bankService" class = "org.springframework.aop.framework.ProxyFactoryBean" > <property name= "target" ref= "bankServiceTarget" /> <property name= "interceptorNames" > <list> <idref bean= "transactionInterceptor" /> </list> </property> </bean> ...... </beans> |
首先,我們配置了一個 TransactionInterceptor 來定義相關的事務規則,他有兩個主要的屬性:一個是 transactionManager,用來指定一個事務管理器,并將具體事務相關的操作委托給它;另一個是 Properties 類型的 transactionAttributes 屬性,它主要用來定義事務規則,該屬性的每一個鍵值對中,鍵指定的是方法名,方法名可以使用通配符,而值就表示相應方法的所應用的事務屬性。
指定事務屬性的取值有較復雜的規則,這在 Spring 中算得上是一件讓人頭疼的事。具體的書寫規則如下:
傳播行為 [,隔離級別] [,只讀屬性] [,超時屬性] [不影響提交的異常] [,導致回滾的異常]
基于 TransactionProxy… 的聲明式事務管理
前面的聲明式事務雖然好,但是卻存在一個非常惱人的問題:配置文件太多。
為了緩解這個問題,Spring 為我們提供了 TransactionProxyFactoryBean,用于將TransactionInterceptor 和 ProxyFactoryBean 的配置合二為一
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
< beans...... > ...... < bean id = "bankServiceTarget" class = "footmark.spring.core.tx.declare.classic.BankServiceImpl" > < property name = "bankDao" ref = "bankDao" /> </ bean > < bean id = "bankService" class = "org.springframework.transaction.interceptor.TransactionProxyFactoryBean" > < property name = "target" ref = "bankServiceTarget" /> < property name = "transactionManager" ref = "transactionManager" /> < property name = "transactionAttributes" > < props > < prop key = "transfer" >PROPAGATION_REQUIRED</ prop > </ props > </ property > </ bean > ...... </ beans > |
這樣子是減少了proxy的代碼,但是每個service還是需要一個配置。所以我們可以使用自動代理的配置,這樣子就減少了大量的配置。也應該是最常用的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
!-- Spring事務管理 --> < bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > < property name = "dataSource" ref = "dataSource" /> </ bean > <!-- 配置事務的傳播特性 --> < bean id = "baseTransactionProxy" class = "org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract = "true" > < property name = "transactionManager" ref = "transactionManager" /> < property name = "transactionAttributes" > < props > < prop key = "add*" >PROPAGATION_REQUIRED</ prop > < prop key = "edit*" >PROPAGATION_REQUIRED</ prop > < prop key = "remove*" >PROPAGATION_REQUIRED</ prop > < prop key = "insert*" >PROPAGATION_REQUIRED</ prop > < prop key = "update*" >PROPAGATION_REQUIRED</ prop > < prop key = "del*" >PROPAGATION_REQUIRED</ prop > < prop key = "*" >readOnly</ prop > </ props > </ property > </ bean > |
基于 命名空間的聲明式事務管理
前面兩種聲明式事務配置方式奠定了 Spring 聲明式事務管理的基石。在此基礎上,Spring 2.x 引入了 命名空間,結合使用 命名空間,帶給開發人員配置聲明式事務的全新體驗,配置變得更加簡單和靈活。另外,得益于 命名空間的切點表達式支持,聲明式事務也變得更加強大。
具體例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
< beans...... > ...... < bean id = "bankService" class = "footmark.spring.core.tx.declare.namespace.BankServiceImpl" > < property name = "bankDao" ref = "bankDao" /> </ bean > < tx:advice id = "bankAdvice" transaction-manager = "transactionManager" > < tx:attributes > < tx:method name = "transfer" propagation = "REQUIRED" /> </ tx:attributes > </ tx:advice > < aop:config > < aop:pointcut id = "bankPointcut" expression = "execution(* *.transfer(..))" /> < aop:advisor advice-ref = "bankAdvice" pointcut-ref = "bankPointcut" /> </ aop:config > ...... </ beans > |
@Transactional 的聲明式事務管理
除了基于命名空間的事務配置方式,Spring 2.x 還引入了基于 Annotation 的方式,具體主要涉及@Transactional 標注。@Transactional 可以作用于接口、接口方法、類以及類方法上。當作用于類上時,該類的所有 public 方法將都具有該類型的事務屬性,同時,我們也可以在方法級別使用該標注來覆蓋類級別的定義。
具體例子:
1
2
3
4
|
@Transactional (propagation = Propagation.REQUIRED) public boolean transfer(Long fromId, Long toId, double amount) { return bankDao.transfer(fromId, toId, amount); } |
但是使用這種我們就必須啟用tx的annotation:
1
|
<tx:annotation-driven transaction-manager= "transactionManager" /> |
附錄
DataSourceTransactionManager(類主要的方法,就不看原代碼了)
設置和獲取DataSource
獲取transaction
transaction是否存在
開始transaction
暫停,釋放連接connection
恢復暫停的連接
提交
回滾
僅回滾
清理
…
HibernateTransactionManager
轉換異常
開始事務
清理
提交
獲取事務
恢復
回滾
僅回滾
暫停
獲取數據源
獲取Entity的Interceptor
獲取SessionFactory
是否存在事務
是否預先提交
總結
本文有關Spring的編程式事務和聲明式事務詳解的介紹就到這里,希望對大家有所幫助。有什么問題可以隨時留言,小編會及時回復大家。在此也非常感謝大家對本站的支持!
原文鏈接:http://blog.csdn.net/kang389110772/article/details/53026247