入口(了解一些基本概念)
Spring事務(wù)屬性(事務(wù)的屬性有哪些?)
我們都知道事務(wù)有開(kāi)始,保存點(diǎn),提交,回滾,隔離級(jí)別等屬性。那么Spring對(duì)于事務(wù)屬性定義有哪些呢?通過(guò)TransactionDefinition接口我們可以了解到:
1
2
3
4
5
6
|
public interface TransactionDefinition{ int getIsolationLevel(); int getPropagationBehavior(); int getTimeout(); boolean isReadOnly(); } |
獲取隔離級(jí)別
獲取傳播特性
獲取超時(shí)
獲取是否只讀
事務(wù)隔離級(jí)別
隔離離別也是通過(guò)TransactionDefinition接口定義的,代表并發(fā)事務(wù)的隔離程度。
隔離級(jí)別 | 描述 |
---|---|
TransactionDefinition.ISOLATION_DEFAULT | 這是默認(rèn)值,表示使用底層數(shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別。對(duì)大部分?jǐn)?shù)據(jù)庫(kù)而言,通常這值就是TransactionDefinition.ISOLATION_READ_COMMITTED |
TransactionDefinition.ISOLATION_READ_UNCOMMITTED | 該隔離級(jí)別表示一個(gè)事務(wù)可以讀取另一個(gè)事務(wù)修改但還沒(méi)有提交的數(shù)據(jù)。該級(jí)別不能防止臟讀和不可重復(fù)讀,因此很少使用該隔離級(jí)別 |
TransactionDefinition.ISOLATION_READ_COMMITTED | 該隔離級(jí)別表示一個(gè)事務(wù)只能讀取另一個(gè)事務(wù)已經(jīng)提交的數(shù)據(jù)。該級(jí)別可以防止臟讀,這也是大多數(shù)情況下的推薦值 |
TransactionDefinition.ISOLATION_REPEATABLE_READ | 該隔離級(jí)別表示一個(gè)事務(wù)在整個(gè)過(guò)程中可以多次重復(fù)執(zhí)行某個(gè)查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢,這些新增的記錄也會(huì)被忽略。該級(jí)別可以防止臟讀和不可重復(fù)讀。 |
TransactionDefinition.ISOLATION_SERIALIZABLE | 所有的事務(wù)依次逐個(gè)執(zhí)行,這樣事務(wù)之間就完全不可能產(chǎn)生干擾,也就是說(shuō),該級(jí)別可以防止臟讀、不可重復(fù)讀以及幻讀。但是這將嚴(yán)重影響程序的性能。通常情況下也不會(huì)用到該級(jí)別。 |
* 事務(wù)傳播行為
所謂事務(wù)的傳播行為是指,如果在開(kāi)始當(dāng)前事務(wù)之前,一個(gè)事務(wù)上下文已經(jīng)存在,此時(shí)有若干選項(xiàng)可以指定一個(gè)事務(wù)性方法的執(zhí)行行為。在TransactionDefinition定義中包括了如下幾個(gè)表示傳播行為的常量.
傳播行為 | 描述 |
---|---|
TransactionDefinition.PROPAGATION_REQUIRED | 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則創(chuàng)建一個(gè)新的事務(wù) |
TransactionDefinition.PROPAGATION_REQUIRES_NEW | 創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起 |
TransactionDefinition.PROPAGATION_SUPPORTS | 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行 |
TransactionDefinition.PROPAGATION_NOT_SUPPORTED | 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則把當(dāng)前事務(wù)掛起 |
TransactionDefinition.PROPAGATION_NEVER | 以非事務(wù)方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常 |
TransactionDefinition.PROPAGATION_MANDATORY | 如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前沒(méi)有事務(wù),則拋出異常 |
TransactionDefinition.PROPAGATION_NESTED | 如果當(dāng)前存在事務(wù),則創(chuàng)建一個(gè)事務(wù)作為當(dāng)前事務(wù)的嵌套事務(wù)來(lái)運(yùn)行;如果當(dāng)前沒(méi)有事務(wù),則該取值等價(jià)于TransactionDefinition.PROPAGATION_REQUIRED |
* 事務(wù)超時(shí)
指定事務(wù)的最大運(yùn)行時(shí)間。使用int指定,單位是秒。
* 事務(wù)的只讀屬性
事務(wù)的只讀屬性是指,對(duì)事務(wù)性資源進(jìn)行只讀操作或者是讀寫操作。所謂事務(wù)性資源就是指那些被事務(wù)管理的資源,比如數(shù)據(jù)源、 JMS 資源,以及自定義的事務(wù)性資源等等。如果確定只對(duì)事務(wù)性資源進(jìn)行只讀操作,那么我們可以將事務(wù)標(biāo)志為只讀的,以提高事務(wù)處理的性能。在 TransactionDefinition 中以 boolean 類型來(lái)表示該事務(wù)是否只讀。
* 事務(wù)的回滾規(guī)則
默認(rèn)出現(xiàn)RuntimeException就會(huì)回滾。如果沒(méi)有拋出任何異常,或者拋出了已檢查異常,則仍然提交事務(wù)。這通常也是大多數(shù)開(kāi)發(fā)者希望的處理方式,也是 EJB 中的默認(rèn)處理方式。(這里個(gè)人理解的是已檢查異常,是我們定義的checkedException,包括我們自定義的異常和調(diào)用方法捕獲的異常)
Spring事務(wù)的三個(gè)基本類
Spring 框架中,涉及到事務(wù)管理的 API 大約有100個(gè)左右,其中最重要的有三個(gè):TransactionDefinition、PlatformTransactionManager、TransactionStatus。所謂事務(wù)管理,其實(shí)就是“按照給定的事務(wù)規(guī)則來(lái)執(zhí)行提交或者回滾操作”。“給定的事務(wù)規(guī)則”就是用 TransactionDefinition 表示的,“按照……來(lái)執(zhí)行提交或者回滾操作”便是用 PlatformTransactionManager 來(lái)表示,而 TransactionStatus 用于表示一個(gè)運(yùn)行著的事務(wù)的狀態(tài)。打一個(gè)不恰當(dāng)?shù)谋扔?,TransactionDefinition 與 TransactionStatus 的關(guān)系就像程序和進(jìn)程的關(guān)系。
TransactionDefinition 定義事務(wù)的屬性
TransactionStatus 定義事務(wù)的狀態(tài)
1
2
3
4
5
|
public interface TransactionStatus{ boolean isNewTransaction(); void setRollbackOnly(); boolean isRollbackOnly(); } |
PlatformTransactionManager 就是各種事務(wù)平臺(tái)的實(shí)現(xiàn)接口
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; } |
所以我們現(xiàn)在可以向下,spring的事務(wù)真正實(shí)現(xiàn)是PlatformTransactionManager的實(shí)現(xiàn)類,通過(guò)各種參數(shù)來(lái)符合TransactionDefinition和TransactionStatus的要求,最后根據(jù)我們編程的或者聲明的進(jìn)行事務(wù)管理。
根據(jù)底層框架的不同(稍后我們看一下DataSourceTransactoinManager和HibernateTransactionManager的代碼,主要做了什么),Spring(或者其他框架)提供主要的實(shí)現(xiàn)如下:
DataSourceTransactionManager: 適合JDBC和ibatis
HibernateTransactionManager: 適合hibernate
JpaTransactionManager: 適用于使用JPA進(jìn)行數(shù)據(jù)持久化操作的情況(更底層的一些)
適用于使用JPA進(jìn)行數(shù)據(jù)持久化操作的情況
到這里基本概念終于結(jié)束了,我們可以介紹兩種類型的事務(wù)管理了
編程式事務(wù)管理
首先我們回想一下不適用spring事務(wù)管理時(shí),hibernate事務(wù)的管理是怎么樣的? 大概是我們手動(dòng)獲取session,獲取transaction,開(kāi)始transaction,提交或者回滾,關(guān)閉session
那么我們使用spring的管理之后,事務(wù)本身控制還是交給持久框架自己管理。知識(shí)spring像一個(gè)代理人一樣,你告訴它,它之后轉(zhuǎn)化后告訴底層框架。
看個(gè)實(shí)際例子吧:
基于底層API的編程式事務(wù)管理
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定義是哪個(gè) private PlatformTransactionManager txManager; //具體使用的txmanager ...... public boolean transfer(Long fromId, Long toId, double amount) { //這里獲取事務(wù)狀態(tài) TransactionStatus txStatus = txManager.getTransaction(txDefinition); boolean result = false ; try { result = bankDao.transfer(fromId, toId, amount); //提交事務(wù) txManager.commit(txStatus); } catch (Exception e) { result = false ; //回滾 txManager.rollback(txStatus); System.out.println( "Transfer Error!" ); } return result; } } |
對(duì)應(yīng)的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的有何不同呢,最多是將事務(wù)層次提高了service層,不限于dao層,所以Spring做了改進(jìn):
基于TransactionTemplate的編程式事務(wù)管理
TransactionTemplate的execute方法提供一個(gè)內(nèi)部匿名類,用來(lái)我們寫transaction代碼,然后提供一個(gè)transactionStatus的參數(shù),這樣你可以控制回滾。這樣一來(lái),我們就不用寫任何關(guān)于事務(wù)API的代碼了。格式大概是 Boolean b = transactionTempate.execute(new TransactionCallBack() { 執(zhí)行方法(TransactionStatus transactionStatus){} },當(dāng)執(zhí)行完成后返回一個(gè)boolean的值. 還有一個(gè)方法,就是不提供返回結(jié)果的。
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) { //調(diào)用一個(gè)回調(diào)函數(shù) 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; } }); } } |
對(duì)應(yīng)的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 > |
從結(jié)果來(lái)看,好像還是不夠簡(jiǎn)單和清晰。下面我們來(lái)看聲明式事務(wù)管理,也是比較推薦的方式
聲明式事務(wù)管理
Spring的聲明式事務(wù)管理是基于AOP的,在方法前和后加上切點(diǎn),用來(lái)打開(kāi)事務(wù)和提交/回滾事務(wù)。
基于TransactionInterceptor的管理
最初,Spring 提供了 TransactionInterceptor 類來(lái)實(shí)施聲明式事務(wù)管理功能
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> |
首先,我們配置了一個(gè) TransactionInterceptor 來(lái)定義相關(guān)的事務(wù)規(guī)則,他有兩個(gè)主要的屬性:一個(gè)是 transactionManager,用來(lái)指定一個(gè)事務(wù)管理器,并將具體事務(wù)相關(guān)的操作委托給它;另一個(gè)是 Properties 類型的 transactionAttributes 屬性,它主要用來(lái)定義事務(wù)規(guī)則,該屬性的每一個(gè)鍵值對(duì)中,鍵指定的是方法名,方法名可以使用通配符,而值就表示相應(yīng)方法的所應(yīng)用的事務(wù)屬性。
指定事務(wù)屬性的取值有較復(fù)雜的規(guī)則,這在 Spring 中算得上是一件讓人頭疼的事。具體的書寫規(guī)則如下:
傳播行為 [,隔離級(jí)別] [,只讀屬性] [,超時(shí)屬性] [不影響提交的異常] [,導(dǎo)致回滾的異常]
基于 TransactionProxy… 的聲明式事務(wù)管理
前面的聲明式事務(wù)雖然好,但是卻存在一個(gè)非常惱人的問(wèn)題:配置文件太多。
為了緩解這個(gè)問(wèn)題,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的代碼,但是每個(gè)service還是需要一個(gè)配置。所以我們可以使用自動(dòng)代理的配置,這樣子就減少了大量的配置。也應(yīng)該是最常用的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
!-- Spring事務(wù)管理 --> < bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager" > < property name = "dataSource" ref = "dataSource" /> </ bean > <!-- 配置事務(wù)的傳播特性 --> < 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 > |
基于 命名空間的聲明式事務(wù)管理
前面兩種聲明式事務(wù)配置方式奠定了 Spring 聲明式事務(wù)管理的基石。在此基礎(chǔ)上,Spring 2.x 引入了 命名空間,結(jié)合使用 命名空間,帶給開(kāi)發(fā)人員配置聲明式事務(wù)的全新體驗(yàn),配置變得更加簡(jiǎn)單和靈活。另外,得益于 命名空間的切點(diǎn)表達(dá)式支持,聲明式事務(wù)也變得更加強(qiáng)大。
具體例子:
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 的聲明式事務(wù)管理
除了基于命名空間的事務(wù)配置方式,Spring 2.x 還引入了基于 Annotation 的方式,具體主要涉及@Transactional 標(biāo)注。@Transactional 可以作用于接口、接口方法、類以及類方法上。當(dāng)作用于類上時(shí),該類的所有 public 方法將都具有該類型的事務(wù)屬性,同時(shí),我們也可以在方法級(jí)別使用該標(biāo)注來(lái)覆蓋類級(jí)別的定義。
具體例子:
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(類主要的方法,就不看原代碼了)
設(shè)置和獲取DataSource
獲取transaction
transaction是否存在
開(kāi)始transaction
暫停,釋放連接connection
恢復(fù)暫停的連接
提交
回滾
僅回滾
清理
…
HibernateTransactionManager
轉(zhuǎn)換異常
開(kāi)始事務(wù)
清理
提交
獲取事務(wù)
恢復(fù)
回滾
僅回滾
暫停
獲取數(shù)據(jù)源
獲取Entity的Interceptor
獲取SessionFactory
是否存在事務(wù)
是否預(yù)先提交
總結(jié)
本文有關(guān)Spring的編程式事務(wù)和聲明式事務(wù)詳解的介紹就到這里,希望對(duì)大家有所幫助。有什么問(wèn)題可以隨時(shí)留言,小編會(huì)及時(shí)回復(fù)大家。在此也非常感謝大家對(duì)本站的支持!
原文鏈接:http://blog.csdn.net/kang389110772/article/details/53026247