Spring 事務(wù)

概念

  • 為了保證對于數(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ù)的問題
    1. 同一個類的方法相互調(diào)用,會使用同一個事務(wù),事務(wù)的傳播行為在同類方法間的調(diào)用失效
    2. 同一個類的方法相互調(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_NEWPROPAGATION_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ù)造成影響
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring 事務(wù)屬性分析 事務(wù)管理對于企業(yè)應(yīng)用而言至關(guān)重要。它保證了用戶的每一次操作都是可靠的,即便出現(xiàn)了異常的...
    壹點零閱讀 1,380評論 0 2
  • 事務(wù)有四個特性:ACID 原子性(Atomicity):事務(wù)是一個原子操作,由一系列動作組成。事務(wù)的原子性確保動作...
    jiangmo閱讀 1,293評論 0 7
  • 很多人喜歡這篇文章,特此同步過來 由淺入深談?wù)搒pring事務(wù) 前言 這篇其實也要歸納到《常識》系列中,但這重點又...
    碼農(nóng)戲碼閱讀 4,912評論 2 59
  • 事務(wù)接口定義 在Spring中,事務(wù)是通過TransactionDefinition接口定義的。其中定義了訪問事務(wù)...
    追夢人Plus閱讀 1,227評論 0 12
  • spring支持編程式事務(wù)管理和聲明式事務(wù)管理兩種方式。 編程式事務(wù)管理使用TransactionTemplate...
    熊熊要更努力閱讀 287評論 0 0

友情鏈接更多精彩內(nèi)容