概念
- 為了保證對于數(shù)據(jù)庫的每一步操作都是可靠的,即使出現(xiàn)了異常情況,也不至于破壞數(shù)據(jù)的完整性。
事務(wù)可以在不同的Service中傳遞
- Spring的事務(wù)本質(zhì)是數(shù)據(jù)庫的事務(wù),事務(wù)的開啟是基于線程的,同步類型方法調(diào)用過程中不會導(dǎo)致事務(wù)失效,但異步方法會失效。
- 注解式開始事務(wù)用的是AOP,故不能用于被同類方法調(diào)用的方法上。
四大特征,或者說保證
- 原子性
(Atomicity)整個事務(wù)的所有操作,要么全部失敗,要么全部成功,不可能停留在某個環(huán)節(jié)(受限于事務(wù)的超時時間),發(fā)生錯誤時會回滾到一切操作執(zhí)行的狀態(tài) - 一致性
(Correspondence)在事務(wù)開始前與結(jié)束后,數(shù)據(jù)庫的完整性約束不被破壞 - 隔離性
(Isolation)隔離狀態(tài)執(zhí)行事務(wù),使它們好像是系統(tǒng)在給定時間內(nèi)執(zhí)行的唯一操作。如果有兩個事務(wù),運行在相同的時間內(nèi),執(zhí)行相同的功能,事務(wù)的隔離性將確保每一事務(wù)在系統(tǒng)中認為只有該事務(wù)在使用系統(tǒng)。這種屬性有時稱為串行化,為了防止事務(wù)操作間的混淆,必須串行化或序列化請 求,使得在同一時間僅有一個請求用于同一數(shù)據(jù)。 - Durability
(Durability)在事務(wù)完成以后,該事務(wù)所對數(shù)據(jù)庫所作的更改便持久的保存在數(shù)據(jù)庫之中,并不會被回滾。
五大隔離級別
TransactionDefinition.ISOLATION_DEFAULT:默認值,表示使用底層數(shù)據(jù)庫的隔離級別。對于大多數(shù)數(shù)據(jù)庫而言,通常此值就是TransactionDefinition.ISOLATION_READ_COMMITTED。TransactionDefinition.ISOLATION_READ_UNCOMMITTED:一個事務(wù)可以讀取另一個事務(wù)已經(jīng)修改但還未提交的數(shù)據(jù),不能防止臟讀與不可重復(fù)讀,因此極少使用TransactionDefinition.ISOLATION_READ_COMMITTED:一個事務(wù)只可以讀取另一個事務(wù)已經(jīng)提交的數(shù)據(jù),可以防止臟讀,為大多數(shù)情況下的推薦值。TransactionDefinition.ISOLATION_REPEATABLE_READ:該隔離級別表示一個事務(wù)在整個過程中可以多次重復(fù)執(zhí)行某個查詢,并且每次返回的記錄都相同。即使在多次查詢之間有新增的數(shù)據(jù)滿足該查詢,這些新增的記錄也會被忽略。該級別可以防止臟讀和不可重復(fù)讀。TransactionDefinition.ISOLATION_SERIALIZABLE:所有事務(wù)串行,可以防止臟讀、不可重復(fù)讀以及幻讀,但會嚴重影響效率不可重復(fù)讀和幻讀區(qū)別:不可重復(fù)讀的重點是修改;同樣的條件,第1次和第2次讀取的值不一樣。 幻讀的重點在于新增或者刪除;同樣的條件, 第1次和第2次讀出來的記錄數(shù)不一樣。
七種傳播行為
傳播行為是指:如果在開始一個事務(wù)之前,已經(jīng)存在一個事務(wù)上下文,此時有若干選項可以指定一個事務(wù)性方法的執(zhí)行行為。在TransactionDefinition定義中包括了如下幾個表示傳播行為的常量:(下面以小紅和她男朋友去上班的故事簡述)
| 參數(shù) | 說明 |
|---|---|
PROPAGATION_REQUIRED |
如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則創(chuàng)建一個新的事務(wù),這是spring默認的傳播行為 |
PROPAGATION_REQUIRES_NEW |
創(chuàng)建一個新的事務(wù),如果當前存在事務(wù),則把當前事務(wù)掛起。 |
PROPAGATION_SUPPORTS |
如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行 |
PROPAGATION_NOT_SUPPORTED |
以非事務(wù)方式運行,如果當前存在事務(wù),則把當前事務(wù)掛起 |
PROPAGATION_NEVER |
以非事務(wù)方式運行,如果當前存在事務(wù),則拋出異常 |
PROPAGATION_MANDATORY |
如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則拋出異常 |
PROPAGATION_NESTED |
如果當前存在事務(wù),則創(chuàng)建一個事務(wù)作為當前事務(wù)的嵌套事務(wù)來運行;如果當前沒有事務(wù),則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED |
TransactionDefinition.PROPAGATION_REQUIRED:如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則創(chuàng)建一個新的事務(wù),這是spring默認的傳播行為。
小紅和她男朋友關(guān)系很好,每天上班男朋友都會開車來接她,但也會有例外情況,所以小紅一般會乘坐男朋友車,如果男朋友沒能來就把自己的車開出來走TransactionDefinition.PROPAGATION_REQUIRES_NEW:創(chuàng)建一個新的事務(wù),如果當前存在事務(wù),則把當前事務(wù)掛起。
小紅與男朋友鬧矛盾了,她決定接下來幾天都自己開車上班,即便男朋友來接她,也要開自己車走,哼╭(╯^╰)╮。TransactionDefinition.PROPAGATION_SUPPORTS:如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則以非事務(wù)的方式繼續(xù)運行。
小紅和男朋友很快和好如初了,于是男朋友來接自己的時候,就會坐男朋友車一起走,可是有天小紅自己開車時車子輪胎壞掉了=-=,只能送到4S店維修了=-=所以男朋友沒能來接自己的時候就只能步行去公司了=-=沒辦法,誰讓自己住的地方偏僻連公交都沒有呢=-=TransactionDefinition.PROPAGATION_NOT_SUPPORTED:以非事務(wù)方式運行,如果當前存在事務(wù),則把當前事務(wù)掛起。
小紅最近伙食有點好,著實有些發(fā)福了,看著日益豐滿的小肚子小紅咬咬牙決定減肥:每天餐飲減半、步行上班,男朋友很是心疼,每天上班時都會來她家接她,不忍心她步行上班,可是她低估了一個姑娘的決心!哼,小紅是絕對不會像資本主義低頭的,步行上班,節(jié)能環(huán)保!TransactionDefinition.PROPAGATION_NEVER:以非事務(wù)方式運行,如果當前存在事務(wù),則拋出異常。
小紅的依然每天來接小紅,小紅害怕自己堅定的意志會被動搖,畢竟暖暖的車廂確實比走路上班舒胡多了鴨,于是小紅告訴男票:再來動搖自己的決心,小心我和你生氣?。。⊕伋霎惓#?/p>TransactionDefinition.PROPAGATION_MANDATORY:如果當前存在事務(wù),則加入該事務(wù);如果當前沒有事務(wù),則拋出異常。
小紅最近來了例假,卻是不能再減肥了,畢竟傷身體的事情還是要杜絕的,可車子還在4S店沒有修好,小紅就給男朋友下了硬性指標:必須來接我上班,否則我就向未來婆婆大人告狀!╭(╯^╰)╮(拋出異常)TransactionDefinition.PROPAGATION_NESTED:如果當前存在事務(wù),則創(chuàng)建一個事務(wù)作為當前事務(wù)的嵌套事務(wù)來運行;如果當前沒有事務(wù),則該取值等價于TransactionDefinition.PROPAGATION_REQUIRED。
小紅最近在網(wǎng)上買了一輛炒雞可耐的自行車,節(jié)能環(huán)保綠色出行的好伙伴吶,小紅愛不釋手于是每天上班都騎車,即便男朋友來接自己也要把自行車放到男票后備箱,待下班后回家路上騎(至于家庭轎車后備箱為什么這么大。。別問我我也不知道~),男票不來就更好咯,自己騎車上班=-=嘟嘟嘟
這里需要指出的是,前面的六種事務(wù)傳播行為是 Spring 從 EJB 中引入的,他們共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 啟動的事務(wù)內(nèi)嵌于外部事務(wù)中(如果存在外部事務(wù)的話),此時,內(nèi)嵌事務(wù)并不是一個獨立的事務(wù),它依賴于外部事務(wù)的存在,只有通過外部的事務(wù)提交,才能引起內(nèi)部事務(wù)的提交,嵌套的子事務(wù)不能單獨提交。如果熟悉 JDBC 中的保存點(SavePoint)的概念,那嵌套事務(wù)就很容易理解了,其實嵌套的子事務(wù)就是保存點的一個應(yīng)用,一個事務(wù)中可以包括多個保存點,每一個嵌套子事務(wù)。另外,外部事務(wù)的回滾也會導(dǎo)致嵌套子事務(wù)的回滾。
事務(wù)超時
所謂事務(wù)超時,就是指一個事務(wù)所允許執(zhí)行的最長時間,如果超過該時間限制但事務(wù)還沒有完成,則自動回滾事務(wù)。在 TransactionDefinition 中以 int 的值來表示超時時間,其單位是秒。
事務(wù)的只讀屬性
事務(wù)的只讀屬性是指,對事務(wù)性資源進行只讀操作或者是讀寫操作。所謂事務(wù)性資源就是指那些被事務(wù)管理的資源,比如數(shù)據(jù)源、 JMS 資源,以及自定義的事務(wù)性資源等等。如果確定只對事務(wù)性資源進行只讀操作,那么我們可以將事務(wù)標志為只讀的,以提高事務(wù)處理的性能。在 TransactionDefinition 中以 boolean 類型來表示該事務(wù)是否只讀。
事務(wù)的回滾規(guī)則
通常情況下,如果在事務(wù)中拋出了未檢查異常(繼承自 RuntimeException 的異常),則默認將回滾事務(wù)。如果沒有拋出任何異常,或者拋出了已檢查異常,則仍然提交事務(wù)。這通常也是大多數(shù)開發(fā)者希望的處理方式,也是 EJB 中的默認處理方式。但是,我們可以根據(jù)需要人為控制事務(wù)在拋出某些未檢查異常時任然提交事務(wù),或者在拋出某些已檢查異常時回滾事務(wù)。
編程式事務(wù)管理
首先配置PlatformTransactionManager的beans
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
其次在要使用事務(wù)的beans中注入PlatformTransactionManager
@Autowired
private PlatformTransactionManager transactionManager;
方法中使用
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// ... 執(zhí)行業(yè)務(wù)
// 執(zhí)行完后提交事務(wù)
transactionManager.commit(status);
} catch(Exception e) {
transactionManager.rollback(status);
}
很少使用了,這里就不在介紹了
聲明式事務(wù)管理
- 基于 <tx> 命名空間的聲明式事務(wù)管理
Spring 2.x 引入了 <tx> 命名空間,結(jié)合使用 <aop> 命名空間,帶給開發(fā)人員配置聲明式事務(wù)的全新體驗,配置變得更加簡單和靈活。另外,得益于 <aop> 命名空間的切點表達式支持,聲明式事務(wù)也變得更加強大。
<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>
如果默認的事務(wù)屬性可以滿足要求,可以使用簡化版:
<bean id="bankService"
class="footmark.spring.core.tx.declare.namespace.BankServiceImpl">
<property name="bankDao" ref="bankDao"/>
</bean>
<tx:advice id="bankAdvice" transaction-manager="transactionManager">
<aop:config>
<aop:pointcut id="bankPointcut" expression="execution(**.transfer(..))"/>
<aop:advisor advice-ref="bankAdvice" pointcut-ref="bankPointcut"/>
</aop:config>
由于使用了切點表達式,我們就不需要針對每一個業(yè)務(wù)類創(chuàng)建一個代理對象了。另外,如果配置的事務(wù)管理器 Bean 的名字取值為“transactionManager”,則我們可以省略 <tx:advice> 的 transaction-manager 屬性,因為該屬性的默認值即為“transactionManager”。
- 基于 @Transactional 的聲明式事務(wù)管理
除了基于命名空間的事務(wù)配置方式,Spring 2.x 還引入了基于 Annotation 的方式,具體主要涉及@Transactional 標注。@Transactional 可以作用于接口、接口方法、類以及類方法上。當作用于類上時,該類的所有 public 方法將都具有該類型的事務(wù)屬性,同時,我們也可以在方法級別使用該標注來覆蓋類級別的定義。
@Transactional(propagation = Propagation.REQUIRED)
public boolean transfer(Long fromId, Long toId, double amount) {
return bankDao.transfer(fromId, toId, amount);
}
Spring 使用 BeanPostProcessor 來處理 Bean 中的標注,因此我們需要在配置文件中作如下聲明來激活該后處理 Bean:
<tx:annotation-driven transaction-manager="transactionManager"/>
與前面相似,transaction-manager 屬性的默認值是 transactionManager,如果事務(wù)管理器 Bean 的名字即為該值,則可以省略該屬性。
雖然 @Transactional 注解可以作用于接口、接口方法、類以及類方法上,但是 Spring 小組建議不要在接口或者接口方法上使用該注解,因為這只有在使用基于接口的代理時它才會生效。另外, @Transactional 注解應(yīng)該只被應(yīng)用到 public 方法上,這是由 Spring AOP 的本質(zhì)決定的。如果你在 protected、private 或者默認可見性的方法上使用 @Transactional 注解,這將被忽略,也不會拋出任何異常。
基于 <tx> 命名空間和基于 @Transactional 的事務(wù)聲明方式各有優(yōu)缺點。基于 <tx> 的方式,其優(yōu)點是與切點表達式結(jié)合,功能強大。利用切點表達式,一個配置可以匹配多個方法,而基于 @Transactional 的方式必須在每一個需要使用事務(wù)的方法或者類上用 @Transactional 標注,盡管可能大多數(shù)事務(wù)的規(guī)則是一致的,但是對 @Transactional 而言,也無法重用,必須逐個指定。另一方面,基于 @Transactional 的方式使用起來非常簡單明了,沒有學(xué)習(xí)成本。開發(fā)人員可以根據(jù)需要,任選其中一種使用,甚至也可以根據(jù)需要混合使用這兩種方式。
- 聲明式事務(wù)的問題
- 同一個類的方法相互調(diào)用,會使用同一個事務(wù),事務(wù)的傳播行為在同類方法間的調(diào)用失效
- 同一個類的方法相互調(diào)用時,假設(shè)A調(diào)用B,若A沒有事務(wù),則B不可能擁有事務(wù)
后記
PROPAGATION_NESTED嵌套事務(wù)與PROPAGATION_REQUIRES_NEW事務(wù)區(qū)別
- 假設(shè)有方法A與方法B, A存在外部事務(wù),B存在內(nèi)部事務(wù),A方法的事務(wù)包含了調(diào)用B方法
public void A() {
// 外部事務(wù)
B();
}
public void B() {
// 內(nèi)部事務(wù)
}
- B方法的內(nèi)部事務(wù)分別使用
PROPAGATION_REQUIRES_NEW與PROPAGATION_NESTED - 這里根據(jù)兩個事務(wù)的不同行為進行比較
-
new_result_B表示使用B方法使用PROPAGATION_REQUIRES_NEW時,B的結(jié)果 -
new_result_A表示使用B方法使用PROPAGATION_REQUIRES_NEW時,A的結(jié)果 -
nested_result_B表示使用B方法使用PROPAGATION_NESTED時,B的結(jié)果 -
nested_result_A表示使用B方法使用PROPAGATION_NESTED時,A的結(jié)果
| B行為 | A行為 | new_result_B | new_result_A | nested_result_B | nested_result_A |
|---|---|---|---|---|---|
| 提交 | 提交 | 成功 | 成功 | 成功 | 成功 |
| 提交 | 回滾 | 成功 | 失敗 | 失敗 | 失敗 |
| 回滾 | 提交 | 失敗 | 成功 | 失敗 | 成功 |
| 回滾 | 回滾 | 失敗 | 失敗 | 失敗 | 失敗 |
- 總結(jié):內(nèi)部嵌套事務(wù),擁有自己的回滾權(quán),但沒有提交權(quán)。
但new事務(wù),既有自己的回滾權(quán),同時具有提交權(quán)。
但相同的是,他們都不會對外部事務(wù)造成影響