Spring事務
事務是邏輯上的一組操做。組成這組操做的各個邏輯單元必須所有完成,若是有一個失敗的話,那麼事務就會回滾到最開始的狀態,彷彿什麼都沒發生過同樣。 java
事務有四個特性:ACID
- 原子性(Atomicity):事務是一個原子操做,由一系列動做組成。事務的原子性確保動做要麼所有完成,要麼徹底不起做用。
- 一致性(Consistency):一旦事務完成(無論成功仍是失敗),系統必須確保它所建模的業務處於一致的狀態,而不會是部分完成部分失敗。在現實中的數據不該該被破壞。
- 隔離性(Isolation):可能有許多事務會同時處理相同的數據,所以每一個事務都應該與其餘事務隔離開來,防止數據損壞。
- 持久性(Durability):一旦事務完成,不管發生什麼系統錯誤,它的結果都不該該受到影響,這樣就能從任何系統崩潰中恢復過來。一般狀況下,事務的結果被寫到持久化存儲器中。
1 傳播行爲
事務的第一個方面是傳播行爲(propagation behavior)。當事務方法被另外一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在本身的事務中運行。Spring定義了七種傳播行爲:mysql
傳播行爲 | 含義 |
---|---|
PROPAGATION_REQUIRED | 表示當前方法必須運行在事務中。若是當前事務存在,方法將會在該事務中運行。不然,會啓動一個新的事務 |
PROPAGATION_SUPPORTS | 表示當前方法不須要事務上下文,可是若是存在當前事務的話,那麼該方法會在這個事務中運行 |
PROPAGATION_MANDATORY | 表示該方法必須在事務中運行,若是當前事務不存在,則會拋出一個異常 |
PROPAGATION_REQUIRED_NEW | 表示當前方法必須運行在它本身的事務中。一個新的事務將被啓動。若是存在當前事務,在該方法執行期間,當前事務會被掛起。若是使用JTATransactionManager的話,則須要訪問TransactionManager |
PROPAGATION_NOT_SUPPORTED | 表示該方法不該該運行在事務中。若是存在當前事務,在該方法運行期間,當前事務將被掛起。若是使用JTATransactionManager的話,則須要訪問TransactionManager |
PROPAGATION_NEVER | 表示當前方法不該該運行在事務上下文中。若是當前正有一個事務在運行,則會拋出異常 |
PROPAGATION_NESTED | 表示若是當前已經存在一個事務,那麼該方法將會在嵌套事務中運行。嵌套的事務能夠獨立於當前事務進行單獨地提交或回滾。若是當前事務不存在,那麼其行爲與PROPAGATION_REQUIRED同樣。注意各廠商對這種傳播行爲的支持是有所差別的。能夠參考資源管理器的文檔來確認它們是否支持嵌套事務 |
(下面是通俗講解) | |
當事務方法被另外一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啓一個新事務,並在本身的事務中運行。Spring定義了七種傳播行爲: |
1》PROPAGATION_REQUIRED 若是存在一個事務,則支持當前事務。若是沒有事務則開啓一個新的事務。sql
2》PROPAGATION_SUPPORTS 若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行。可是對於事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少量不一樣。數據庫
3》PROPAGATION_MANDATORY 若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常。編程
4》PROPAGATION_REQUIRES_NEW 老是開啓一個新的事務。若是一個事務已經存在,則將這個存在的事務掛起。後端
我把ts1稱爲外層事務,ts2稱爲內層事務。從上面的代碼能夠看出,ts2與ts1是兩個獨立的事務,互不相干。Ts2是否成功並不依賴於 ts1。若是methodA方法在調用methodB方法後的doSomeThingB方法失敗了,而methodB方法所作的結果依然被提交。而除了 methodB以外的其它代碼致使的結果卻被回滾了。使用PROPAGATION_REQUIRES_NEW,須要使用JtaTransactionManager做爲事務管理器。併發
5》PROPAGATION_NOT_SUPPORTED 老是非事務地執行,並掛起任何存在的事務。使用PROPAGATION_NOT_SUPPORTED,也須要使用JtaTransactionManager做爲事務管理器。學習
6》PROPAGATION_NEVER 老是非事務地執行。表示當前方法不該該運行在事務上下文中。若是當前正有一個事務在運行,則會拋出異常。(當事務方法被另外一個事務方法調用時)若是存在一個活動事務,則拋出異常。優化
7》PROPAGATION_NESTED若是一個活動的事務存在,則運行在一個嵌套的事務中. 若是沒有活動事務, 則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。.net
這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager做爲事務管理器。須要JDBC 驅動的java.sql.Savepoint類。
有一些JTA的事務管理器實現可能也提供了一樣的功能。使用PROPAGATION_NESTED,還須要把PlatformTransactionManager的nestedTransactionAllowed屬性設爲true;而 nestedTransactionAllowed屬性值默認爲false。
嵌套事務一個很是重要的概念就是內層事務依賴於外層事務。外層事務失敗時,會回滾內層事務所作的動做。而內層事務操做失敗並不會引發外層事務的回滾。
注意:
1》PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別:它們很是相似,都像一個嵌套事務,若是不存在一個活動的事務,都會開啓一個新的事務。使用 PROPAGATION_REQUIRES_NEW時,內層事務與外層事務就像兩個獨立的事務同樣,一旦內層事務進行了提交後,外層事務不能對其進行回滾。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它須要JTA事務管理器的支持。
2》使用PROPAGATION_NESTED時,外層事務的回滾能夠引發內層事務的回滾。而內層事務的異常並不會致使外層事務的回滾,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED時,須要JDBC 3.0以上驅動及1.4以上的JDK版本支持。其它的JTA TrasactionManager實現可能有不一樣的支持方式。
3》PROPAGATION_REQUIRES_NEW 啓動一個新的, 不依賴於環境的 「內部」 事務. 這個事務將被徹底 commited 或 rolled back 而不依賴於外部事務, 它擁有本身的隔離範圍, 本身的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行。
4》另外一方面, PROPAGATION_NESTED 開始一個 「嵌套的」 事務, 它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, 它將取得一個 savepoint. 若是這個嵌套事務失敗, 咱們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束後它纔會被提交。
5》因而可知, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在於, PROPAGATION_REQUIRES_NEW 徹底是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 若是外部事務 commit, 嵌套事務也會被 commit, 這個規則一樣適用於 roll back.
6》PROPAGATION_REQUIRED應該是咱們首先的事務傳播行爲。它可以知足咱們大多數的事務需求。
2 隔離級別
事務的第二個維度就是隔離級別(isolation level)。隔離級別定義了一個事務可能受其餘併發事務 影響的程度。
併發事務引發的問題
在典型的應用程序中,多個事務併發運行,常常會操做相同的數據來完成各自的任務。併發雖然是必須的,但可能會致使如下的問題:
- 髒讀(Dirty reads)——髒讀發生在一個事務讀取了另外一個事務改寫但還沒有提交的數據時。若是改寫在稍後被回滾了,那麼第一個事務獲取的數據就是無效的。
- 不可重複讀(Nonrepeatable read)——不可重複讀發生在一個事務執行相同的查詢兩次或兩次以上,可是每次都獲得不一樣的數據時。這一般是由於另外一個併發事務在兩次查詢期間進行了更新。
- 幻讀(Phantom read)——幻讀與不可重複讀相似。它發生在一個事務(T1)讀取了幾行數據,接着另外一個併發事務(T2)插入了一些數據時。在隨後的查詢中,第一個事務(T1)就會發現多了一些本來不存在的記錄。
不可重複讀與幻讀的區別
- 不可重複讀的重點是修改:一樣的條件, 你讀取過的數據, 再次讀取出來發現值不同了
- 幻讀的重點在於新增或者刪除:一樣的條件, 第1次和第2次讀出來的記錄數不同。
- 從總的結果來看, 彷佛不可重複讀和幻讀都表現爲兩次讀取的結果不一致。但若是你從控制的角度來看, 二者的區別就比較大。 對於前者, 只須要鎖住知足條件的記錄。 對於後者, 要鎖住知足條件及其相近的記錄。
隔離級別 | 含義 |
---|---|
ISOLATION_DEFAULT | 使用後端數據庫默認的隔離級別 |
ISOLATION_READ_UNCOMMITTED | 最低的隔離級別,容許讀取還沒有提交的數據變動,可能會致使髒讀、幻讀或不可重複讀 |
ISOLATION_READ_COMMITTED | 容許讀取併發事務已經提交的數據,能夠阻止髒讀,可是幻讀或不可重複讀仍有可能發生 |
ISOLATION_REPEATABLE_READ | 對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改,能夠阻止髒讀和不可重複讀,但幻讀仍有可能發生 |
ISOLATION_SERIALIZABLE | 最高的隔離級別,徹底服從ACID的隔離級別,確保阻止髒讀、不可重複讀以及幻讀,也是最慢的事務隔離級別,由於它一般是經過徹底鎖定事務相關的數據庫表來實現的 |
對上表的一些解釋以下: |
1》ISOLATION_DEFAULT 使用後端數據庫默認的隔離級別。(ISOLATION [ˌaɪsəˈleɪʃn] 隔離)
2》ISOLATION_READ_UNCOMMITTED 最低的隔離級別,容許讀取還沒有提交的數據變動,可能會致使髒讀、幻讀或不可重複讀
3》ISOLATION_READ_COMMITTED 容許讀取併發事務已經提交的數據,能夠阻止髒讀,可是幻讀或不可重複讀仍有可能發生。(髒讀:發生在 一個事務讀取了,另外一個事務改寫了但還沒有提交到數據庫的數據時。由於改寫沒有提交到數據庫,因此可能會回滾。致使第一個事務讀到的值無效)
4》ISOLATION_REPEATABLE_READ 一個事務中,對同一字段的屢次讀取結果都是一致的,除非數據是被自己事務本身所修改。能夠阻止髒讀和不可重複讀,但幻讀仍有可能發生(不可重複讀:一個事務中執行屢次相同的查詢時,讀取到的值不一樣,重點在於修改update)
5》ISOLATION_SERIALIZABLE 最高的隔離級別,徹底服從ACID的隔離級別,確保阻止髒讀、不可重複讀以及幻讀,也是最慢的事務隔離級別,由於它一般是經過徹底鎖定事務相關的數據庫表來實現的。(幻讀:一個事務中,2次或屢次執行相同的查詢時,獲得的結果的行數不同。重點在於增長insert和刪除delete)
3 只讀
事務的第三個特性是它是否爲只讀事務。若是事務只對後端的數據庫進行該操做,數據庫能夠利用事務的只讀特性來進行一些特定的優化。經過將事務設置爲只讀,你就能夠給數據庫一個機會,讓它應用它認爲合適的優化措施。
4 事務超時
爲了使應用程序很好地運行,事務不能運行太長的時間。由於事務可能涉及對後端數據庫的鎖定,因此長時間的事務 會沒必要要地佔用數據庫資源。事務超時就是事務的一個定時器,在特定時間內事務若是沒有執行完畢,那麼就會自動回滾,而不是一直等待其結束。
5 回滾規則
事務五邊形的最後一個方面是一組規則,這些規則定義了哪些異常會致使事務回滾,而哪些不會。默認狀況下,事務只有遇到運行期異常時纔會回滾,而在遇到檢查型異常時不會回滾(這一行爲與EJB的回滾行爲是一致的) 可是你能夠聲明事務在遇到特定的檢查型異常時像遇到運行期異常那樣回滾。一樣,你還能夠聲明事務遇到特定的異常不回滾,即便這些異常是運行期異常。
編程式事務
Spring提供了對編程式事務和聲明式事務的支持。編程式事務容許用戶在代碼中精肯定義事務的邊界,而聲明式事務(基於AOP)有助於用戶將操做與事務規則進行解耦。
簡單地說,編程式事務侵入到了業務代碼裏面,可是提供了更加詳細的事務管理;而聲明式事務因爲基於AOP,因此既能起到事務管理的做用,又能夠不影響業務代碼的具體實現。(編程式事務放到了代碼裏。聲明式事務放在AOP裏面)
看到這裏別忘記點個小小的贊喔~ 更多往期文章我已整合成PDF放在了個人社區,也整理了一些Java學習的zl,須要的小夥伴點擊傳送門