有關Spring事務,看這一篇就足夠了
- 2019 年 10 月 3 日
- 筆記
本文將按照聲明式事務的五個特性進行介紹:
- 事務傳播機制
- 事務隔離機制
- 只讀
- 事務超時
- 回滾規則
Spring事務傳播機制
事務的特性
- 原子性(Atomicity):事務是一個原子操作,由一系列動作組成。事務的原子性確保動作要麼全部完成,要麼完全不起作用。
- 一致性(Consistency):一旦事務完成(不管成功還是失敗),系統必須確保它所建模的業務處於一致的狀態,而不會是部分完成部分失敗。在現實中的數據不應該被破壞。
- 隔離性(Isolation):可能有許多事務會同時處理相同的數據,因此每個事務都應該與其他事務隔離開來,防止數據損壞。
- 持久性(Durability):一旦事務完成,無論發生什麼系統錯誤,它的結果都不應該受到影響,這樣就能從任何系統崩潰中恢復過來。通常情況下,事務的結果被寫到持久化存儲器中。
Spring事務的配置方式
Spring支援編程式事務管理以及聲明式事務管理兩種方式。
1. 編程式事務管理
編程式事務管理是侵入性事務管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,對於編程式事務管理,Spring推薦使用TransactionTemplate。
2. 聲明式事務管理
聲明式事務管理建立在AOP之上,其本質是對方法前後進行攔截,然後在目標方法開始之前創建或者加入一個事務,執行完目標方法之後根據執行的情況提交或者回滾。
編程式事務每次實現都要單獨實現,但業務量大功能複雜時,使用編程式事務無疑是痛苦的,而聲明式事務不同,聲明式事務屬於無侵入式,不會影響業務邏輯的實現,只需要在配置文件中做相關的事務規則聲明或者通過註解的方式,便可以將事務規則應用到業務邏輯中。
顯然聲明式事務管理要優於編程式事務管理,這正是Spring倡導的非侵入式的編程方式。唯一不足的地方就是聲明式事務管理的粒度是方法級別,而編程式事務管理是可以到程式碼塊的,但是可以通過提取方法的方式完成聲明式事務管理的配置。
事務的傳播機制
事務的傳播性一般用在事務嵌套的場景,比如一個事務方法裡面調用了另外一個事務方法,那麼兩個方法是各自作為獨立的方法提交還是內層的事務合併到外層的事務一起提交,這就是需要事務傳播機制的配置來確定怎麼樣執行。
常用的事務傳播機制如下:
- PROPAGATION_REQUIRED
Spring默認的傳播機制,能滿足絕大部分業務需求,如果外層有事務,則當前事務加入到外層事務,一塊提交,一塊回滾。如果外層沒有事務,新建一個事務執行 - PROPAGATION_REQUES_NEW
該事務傳播機制是每次都會新開啟一個事務,同時把外層事務掛起,噹噹前事務執行完畢,恢復上層事務的執行。如果外層沒有事務,執行當前新開啟的事務即可 - PROPAGATION_SUPPORT
如果外層有事務,則加入外層事務,如果外層沒有事務,則直接使用非事務方式執行。完全依賴外層的事務 - PROPAGATION_NOT_SUPPORT
該傳播機制不支援事務,如果外層存在事務則掛起,執行完當前程式碼,則恢復外層事務,無論是否異常都不會回滾當前的程式碼 - PROPAGATION_NEVER
該傳播機制不支援外層事務,即如果外層有事務就拋出異常 - PROPAGATION_MANDATORY
與NEVER相反,如果外層沒有事務,則拋出異常 - PROPAGATION_NESTED
該傳播機制的特點是可以保存狀態保存點,當前事務回滾到某一個點,從而避免所有的嵌套事務都回滾,即各自回滾各自的,如果子事務沒有把異常吃掉,基本還是會引起全部回滾的。
傳播規則回答了這樣一個問題:一個新的事務應該被啟動還是被掛起,或者是一個方法是否應該在事務性上下文中運行。
事務的隔離級別
事務的隔離級別定義一個事務可能受其他並發務活動活動影響的程度,可以把事務的隔離級別想像為這個事務對於事物處理數據的自私程度。
在一個典型的應用程式中,多個事務同時運行,經常會為了完成他們的工作而操作同一個數據。並發雖然是必需的,但是會導致以下問題:
- 臟讀(Dirty read)
臟讀發生在一個事務讀取了被另一個事務改寫但尚未提交的數據時。如果這些改變在稍後被回滾了,那麼第一個事務讀取的數據就會是無效的。 -
不可重複讀(Nonrepeatable read)
不可重複讀發生在一個事務執行相同的查詢兩次或兩次以上,但每次查詢結果都不相同時。這通常是由於另一個並發事務在兩次查詢之間更新了數據。不可重複讀重點在修改。
-
幻讀(Phantom reads)
幻讀和不可重複讀相似。當一個事務(T1)讀取幾行記錄後,另一個並發事務(T2)插入了一些記錄時,幻讀就發生了。在後來的查詢中,第一個事務(T1)就會發現一些原來沒有的額外記錄。幻讀重點在新增或刪除。
在理想狀態下,事務之間將完全隔離,從而可以防止這些問題發生。然而,完全隔離會影響性能,因為隔離經常涉及到鎖定在資料庫中的記錄(甚至有時是鎖表)。完全隔離要求事務相互等待來完成工作,會阻礙並發。因此,可以根據業務場景選擇不同的隔離級別。
| 隔離級別 | 含義 |
|---|---|
| ISOLATION_DEFAULT | 使用後端資料庫默認的隔離級別 |
| ISOLATION_READ_UNCOMMITTED | 允許讀取尚未提交的更改。可能導致臟讀、幻讀或不可重複讀。 |
| ISOLATION_READ_COMMITTED | (Oracle 默認級別)允許從已經提交的並發事務讀取。可防止臟讀,但幻讀和不可重複讀仍可能會發生。 |
| ISOLATION_REPEATABLE_READ | (MYSQL默認級別)對相同欄位的多次讀取的結果是一致的,除非數據被當前事務本身改變。可防止臟讀和不可重複讀,但幻讀仍可能發生。 |
| ISOLATION_SERIALIZABLE | 完全服從ACID的隔離級別,確保不發生臟讀、不可重複讀和幻影讀。這在所有隔離級別中也是最慢的,因為它通常是通過完全鎖定當前事務所涉及的數據表來完成的。 |
只讀
如果一個事務只對資料庫執行讀操作,那麼該資料庫就可能利用那個事務的只讀特性,採取某些優化措施。通過把一個事務聲明為只讀,可以給後端資料庫一個機會來應用那些它認為合適的優化措施。由於只讀的優化措施是在一個事務啟動時由後端資料庫實施的, 因此,只有對於那些具有可能啟動一個新事務的傳播行為(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、 ROPAGATION_NESTED)的方法來說,將事務聲明為只讀才有意義。
事務超時
為了使一個應用程式很好地執行,它的事務不能運行太長時間。因此,聲明式事務的下一個特性就是它的超時。
假設事務的運行時間變得格外的長,由於事務可能涉及對資料庫的鎖定,所以長時間運行的事務會不必要地佔用資料庫資源。這時就可以聲明一個事務在特定秒數後自動回滾,不必等它自己結束。
由於超時時鐘在一個事務啟動的時候開始的,因此,只有對於那些具有可能啟動一個新事務的傳播行為(PROPAGATION_REQUIRES_NEW、PROPAGATION_REQUIRED、ROPAGATION_NESTED)的方法來說,聲明事務超時才有意義。
回滾規則
在默認設置下,事務只在出現運行時異常(runtime exception)時回滾,而在出現受檢查異常(checked exception)時不回滾(這一行為和EJB中的回滾行為是一致的)。
不過,可以聲明在出現特定受檢查異常時像運行時異常一樣回滾。同樣,也可以聲明一個事務在出現特定的異常時不回滾,即使特定的異常是運行時異常。
Spring聲明式事務配置參考
事物配置中有哪些屬性可以配置?以下只是簡單的使用參考
- 事務的傳播性:
@Transactional(propagation=Propagation.REQUIRED) - 事務的隔離級別:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
讀取未提交數據(會出現臟讀, 不可重複讀) 基本不使用
- 只讀:
@Transactional(readOnly=true)
該屬性用於設置當前事務是否為只讀事務,設置為true表示只讀,false則表示可讀寫,默認值為false。 - 事務的超時性:
@Transactional(timeout=30) - 回滾:
指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)
指定多個異常類:@Transactional(rollbackFor={RuntimeException.class, Exception.class})
該屬性用於設置需要進行回滾的異常類數組,當方法中拋出指定異常數組中的異常時,則進行事務回滾。
最後,限於筆者經驗水平有限,歡迎讀者就文中的觀點提出寶貴的建議和意見。如果想獲得更多的學習資源或者想和更多的技術愛好者一起交流,可以關注我的公眾號『全菜工程師小輝』後台回復關鍵詞領取學習資料、進入前後端技術交流群和程式設計師副業群。同時也可以加入程式設計師副業群Q群:735764906 一起交流。

