【Spring】事務的實現方式


原文鏈接:https://tidyko.com/posts/c87c55c4.html

1 初步理解

  理解事務之前,先講一個你日常生活中最常干的事:轉賬。
  場景設定:
    用戶名  余額
    A     1000
    B     1000
  操作:
    A通過支付寶給B轉賬200塊,做這件事情會進行兩個操作。
    1:A賬號-200
    2:B賬號+200
  結果:
    1:所以如果成功進行了一次轉賬操作的話,得到的數據應該是如下:
      用戶名   余額
      A      800
      B      1200
    2:但是如果是在失敗的情況下,沒有做事務處理的話,可能會得到一種情況就是:
      用戶名   余額
      A      800
      B      1000
      從上面的數據看,A賬號成功扣除了200之后,由於某些不可預測的原因,中斷了交易,這時候從A賬號扣除的200塊並未成功加入到B賬號當中。
  總結:
    從上面的失敗案例看我們理想的情況是,如果轉賬失敗的情況下應該是A賬號沒有扣除200塊,同時B賬號也不會增加200塊,即以下所示:
    用戶名   余額
    A      1000
    B      1000
  所以,這里我們提出了一個概念:事務。
  事務就是用來解決類似問題的。事務是一系列的動作,它們綜合在一起才是一個完整的工作單元,這些動作必須全部完成,如果有一個失敗的話,那么事務就會回滾到 最開始的狀態,仿佛什么都沒發生過一樣。 
  在企業級應用程序開發中,事務管理必不可少的技術,用來確保數據的完整性和一致性。 
  事務有四個特性:ACID
      • 原子性(Atomicity):事務是一個原子操作,由一系列動作組成。事務的原子性確保動作要么全部完成,要么完全不起作用。
      • 一致性(Consistency):不管操作是成功還是失敗,事務必須保持數據前后的一致性,比如A給B轉賬,兩個人的總金額合計2000塊,轉賬前與轉賬后,兩人的總金額合計都必須是2000塊。
      • 隔離性(Isolation):可能有許多事務會同時處理相同的數據,因此每個事務都應該與其他事務隔離開來,防止數據損壞,避免出現臟讀,幻讀,不可重復讀的狀況。比如多筆轉賬同時進行了操作,每筆轉賬的事務應該是隔離的。
      • 持久性(Durability):一旦事務完成,無論發生什么系統錯誤,它的結果都不應該受到影響,這樣就能從任何系統崩潰中恢復過來。通常情況下,事務的結果被寫到持久化存儲器中。

2 核心接口

Spring事務管理的實現有許多細節,如果對整個接口框架有個大體了解會非常有利於我們理解事務,下面通過講解Spring的事務接口來了解Spring實現事務的具體策略。 
Spring事務管理涉及的接口的聯系如下:

2.1 平台事務管理器(PlatformTransactionManager)

Spring並不直接管理事務,而是提供了多種事務管理器,他們將事務管理的職責委托給Hibernate或者JTA等持久化機制所提供的相關平台框架的事務來實現。 
Spring事務管理器的接口是org.springframework.transaction.PlatformTransactionManager,
通過這個接口,Spring為各個平台如JDBC、Hibernate等都提供了對應的事務管理器,但是具體的實現就是各個平台自己的事情了。
PlatformTransactionManager接口的內容如上圖:
主要定義了
getTransaction(TransactionDefinition definition) //獲取事務,
commit() //提交,
rollback() //回滾
方法。
而具體實現,我們常用的有:
DataSourceTransactionManager:使用jdbc來進行數據庫操作時,對事務進行管理, 
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
HibernateTransactionManager:使用hibernate進行操作時,對事務進行管理。
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory" /> </bean>

2.2 基礎事務屬性定義(TransactionDefinition)

上面講到的事務管理器接口PlatformTransactionManager通過getTransaction(TransactionDefinition definition)方法
傳入事務屬性定義來得到事務,
這個方法里面的參數是TransactionDefinition類,這個類就定義了一些基本的事務屬性。 
那么什么是事務屬性呢?
事務屬性可以理解成事務的一些基本配置,描述了事務策略如何應用到方法上。事務屬性包含了5個方面,如圖所示:
TransactionDefinition接口的內容如上圖:
主要定義了
getPropagationBehavior() //獲取事務傳播行為,
getIsolationLevel() //獲取事務隔離級別,
getTimeout() //獲取超時時間,
isReadOnly() //事務是否只讀
我們可以發現TransactionDefinition正好用來定義事務屬性,下面詳細介紹一下各個事務屬性。

2.2.1 傳播行為

事務的第一個方面是傳播行為(propagation behavior)。
當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。
Spring定義了七種傳播行為:
傳播行為
含義
PROPAGATION_REQUIRED
表示當前方法必須運行在事務中。如果當前事務存在,方法將會在該事務中運行。否則,會啟動一個新的事務
PROPAGATION_SUPPORTS
表示當前方法不需要事務上下文,但是如果存在當前事務的話,那么該方法會在這個事務中運行
PROPAGATION_MANDATORY
表示該方法必須在事務中運行,如果當前事務不存在,則會拋出一個異常
PROPAGATION_REQUIRED_NEW
表示當前方法必須運行在它自己的事務中。一個新的事務將被啟動。如果存在當前事務,在該方法執行期間,當前事務會被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager
PROPAGATION_NOT_SUPPORTED
表示該方法不應該運行在事務中。如果存在當前事務,在該方法運行期間,當前事務將被掛起。如果使用JTATransactionManager的話,則需要訪問TransactionManager
PROPAGATION_NEVER
表示當前方法不應該運行在事務上下文中。如果當前正有一個事務在運行,則會拋出異常
PROPAGATION_NESTED
表示如果當前已經存在一個事務,那么該方法將會在嵌套事務中運行。嵌套的事務可以獨立於當前事務進行單獨地提交或回滾。如果當前事務不存在,那么其行為與PROPAGATION_REQUIRED一樣。注意各廠商對這種傳播行為的支持是有所差異的。可以參考資源管理器的文檔來確認它們是否支持嵌套事務
可以看到以上有七種事務傳播行為,經常使用到的為標注了紅色的三種。REQUIRED是使用最頻繁的一個。

2.2.2 隔離級別

事務的第二個維度就是隔離級別(isolation level)。
隔離級別定義了一個事務可能受其他並發事務影響的程度。 
Spring定義了五種隔離級別:
隔離級別
含義
ISOLATION_DEFAULT
使用后端數據庫默認的隔離級別
ISOLATION_READ_UNCOMMITTED
最低的隔離級別,允許讀取尚未提交的數據變更,可能會導致臟讀、幻讀或不可重復讀
ISOLATION_READ_COMMITTED
允許讀取並發事務已經提交的數據,可以阻止臟讀,但是幻讀或不可重復讀仍有可能發生
ISOLATION_REPEATABLE_READ
對同一字段的多次讀取結果都是一致的,除非數據是被本身事務自己所修改,可以阻止臟讀和不可重復讀,但幻讀仍有可能發生
ISOLATION_SERIALIZABLE
最高的隔離級別,完全服從ACID的隔離級別,確保阻止臟讀、不可重復讀以及幻讀,也是最慢的事務隔離級別,因為它通常是通過完全鎖定事務相關的數據庫表來實現的
Spring中,默認使用DEFAULT,即當前連接池中使用的數據庫的隔離級別。
Oracle默認的隔離級別為:READ_COMMITTED
Mysql默認的隔離級別為:REPEATABLE_READ

2.2.3 只讀

事務的第三個特性是它是否為只讀事務。
如果事務只對后端的數據庫進行該操作,數據庫可以利用事務的只讀特性來進行一些特定的優化。
通過將事務設置為只讀,你就可以給數據庫一個機會,讓它應用它認為合適的優化措施。

2.2.4 事務超時

為了使應用程序很好地運行,事務不能運行太長的時間。
因為事務可能涉及對后端數據庫的鎖定,所以長時間的事務會不必要的占用數據庫資源。
事務超時就是事務的一個定時器,在特定時間內事務如果沒有執行完畢,那么就會自動回滾,而不是一直等待其結束。

2.2.5 回滾規則

事務五邊形的最后一個方面是一組規則,這些規則定義了哪些異常會導致事務回滾而哪些不會。
默認情況下,事務只有遇到運行期異常時才會回滾,而在遇到檢查型異常時不會回滾(這一行為與EJB的回滾行為是一致的) 
但是你可以聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。同樣,你還可以聲明事務遇到特定的異常不回滾,即使這些異常是運行期異常。

2.3 事務狀態

上面講到的調用PlatformTransactionManager接口的getTransaction()的方法得到的是TransactionStatus接口的一個實現。
TransactionStatus接口的內容如上圖:
主要定義了
isNewTransaction(); // 是否是新的事物
hasSavepoint(); // 是否有恢復點
setRollbackOnly(); // 設置為只回滾
isRollbackOnly(); // 是否為只回滾
isCompleted; // 是否已完成
可以發現這個接口描述的是一些處理事務提供簡單的控制事務執行和查詢事務狀態的方法,在回滾或提交的時候需要應用對應的事務狀態。

3 事務的實現方式

3.1 編程式和聲明式事務的區別

Spring提供了編程式事務和聲明式事務兩種實現方式,
編程式事務允許用戶在代碼中精確定義事務的邊界,
而聲明式事務(基於AOP)有助於用戶將操作與事務規則進行解耦。 
簡單地說,編程式事務侵入到了業務代碼里面,但是提供了更加詳細的事務管理;而聲明式事務由於基於AOP,所以既能起到事務管理的作用,又可以不影響業務代碼的具體實現。
 
代碼演示:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM