摘要:使用需要使用作為事務管理器。兩個事務互不影響。這是默認的隔離級別,使用數據庫默認的事務隔離級別下邊的四個與的隔離級別相對應這是事務最低的隔離級別,它充許另外一個事務可以看到這個事務未提交的數據。這種事務隔離級別可
Spring事務整理
工作了幾年了,今天抽時間整理一下spring的事務,說起spring的事務是面試的時候面試官經常提及的問題,接下來結合網上資料再總結下spring的事務
spring事務在很多小型的公司都不怎么考慮的,新入行的小伙伴們也很少能接觸到這些東西。也不排除有喜愛學習的同學利用業余時間自學這塊東西,廢話不多。直接進入今天的正題
提到spring事務就必須提到Spring AOP,提到AOP又涉及到動態代理,這些內容今天就不討論了。主要還是以spring事務為主來總結。
事務有四個特性:ACID
一、Spring事務的核心接口原子性(Atomicity):事務是一個原子操作,由一系列動作組成。事務的原子性確保動作要么全部完成,要么完全不起作用。
一致性(Consistency):一旦事務完成(不管成功還是失敗),系統必須確保它所建模的業務處于一致的狀態,而不會是部分完成部分失 敗。在現實中的數據不應該被破壞。
隔離性(Isolation):可能有許多事務會同時處理相同的數據,因此每個事務都應該與其他事務隔離開來,防止數據損壞。
持久性(Durability):一旦事務完成,無論發生什么系統錯誤,它的結果都不應該受到影響,這樣就能從任何系統崩潰中恢復過來。通常情況下,事務的結果被寫到持久化存儲器中。
Spring在TransactionDefinition接口中定義這些屬性,以供PlatfromTransactionManager使用, PlatfromTransactionManager是spring事務管理的核心接口。
二、事務的基本原理Spring事務的本質其實就是數據庫對事務的支持,沒有數據庫的事務支持,spring是無法提供事務功能的。
對于純JDBC操作數據庫,想要用到事務,可以按照以下步驟進行:
1、獲取連接 Connection con = DriverManager.getConnection()
2、開啟事務con.setAutoCommit(true/false);
3、執行CRUD
4、提交事務/回滾事務 con.commit() / con.rollback();
5、關閉連接 conn.close();
使用Spring的事務管理功能后,我們可以不再寫步驟 2 和 4 的代碼,而是由Spirng 自動完成。那么Spring是如何在我們書寫的 CRUD 之前和之后開啟事務和關閉事務的呢?解決這個問題,也就可以從整體上理解Spring的事務管理實現原理了。下面簡單地介紹下,注解方式為例子
1、配置文件開啟注解驅動,在相關的類和方法上通過注解@Transactional標識。
2、spring 在啟動的時候會去解析生成相關的bean,這時候會查看擁有相關注解的類和方法,并且為這些類和方法生成代理,并根據@Transaction的相關參數進行相關配置注入,這樣就在代理中為我們把相關的事務處理掉了(開啟正常提交事務,異常回滾事務)。
3、真正的數據庫層的事務提交和回滾是通過binlog或者redo log實現的。
上面講到的事務管理器接口PlatformTransactionManager通過getTransaction(TransactionDefinition definition)方法來得到事務,這個方法里面的參數是TransactionDefinition類,這個類就定義了一些基本的事務屬性。
那么什么是事務屬性呢?事務屬性可以理解成事務的一些基本配置,描述了事務策略如何應用到方法上。事務屬性包含了5個方面,如圖所示:
而TransactionDefinition接口內容如下:
public interface TransactionDefinition { int getPropagationBehavior(); // 返回事務的傳播行為 int getIsolationLevel(); // 返回事務的隔離級別,事務管理器根據它來控制另外一個事務可以看到本事務內的哪些數據 int getTimeout(); // 返回事務必須在多少秒內完成 boolean isReadOnly(); // 事務是否只讀,事務管理器能夠根據這個返回值進行優化,確保事務是只讀的 }
我們可以發現TransactionDefinition正好用來定義事務屬性,下面詳細介紹一下各個事務屬性。
二、事務的傳播行為(在TransactionDefinition接口中定義了七個事務傳播行為)事務的第一個方面是傳播行為(propagation behavior)。當事務方法被另一個事務方法調用時,必須指定事務應該如何傳播。例如:方法可能繼續在現有事務中運行,也可能開啟一個新事務,并在自己的事務中運行。Spring定義了七種傳播行為:
(1)PROPAGATION_REQUIRED 如果存在一個事務,則支持當前事務。如果沒有事務則開啟一個新的事務。
Java代碼:
//事務屬性 PROPAGATION_REQUIRED methodA{ …… methodB(); …… } //事務屬性 PROPAGATION_REQUIRED methodB{ …… }
tips:使用spring聲明式事務,spring使用AOP來支持聲明式事務,會根據事務屬性,自動在方法調用之前決定是否開啟一個事務,并在方法執行之后決定事務提交或回滾事務。
多帶帶調用methodB方法:
Java代碼:
main{ metodB(); }
相當于
Java代碼
Main{ Connection con=null; try{ con = getConnection(); con.setAutoCommit(false); //方法調用 methodB(); //提交事務 con.commit(); } Catch(RuntimeException ex){ //回滾事務 con.rollback(); } finally{ //釋放資源 closeCon(); } }
Spring保證在methodB方法中所有的調用都獲得到一個相同的連接。在調用methodB時,沒有一個存在的事務,所以獲得一個新的連接,開啟了一個新的事務。
多帶帶調用MethodA時,在MethodA內又會調用MethodB.
執行效果相當于:
Java代碼:
main{ Connection con = null; try{ con = getConnection(); methodA(); con.commit(); } catch(RuntimeException ex){ con.rollback(); } finally{ closeCon(); } }
調用MethodA時,環境中沒有事務,所以開啟一個新的事務.當在MethodA中調用MethodB時,環境中已經有了一個事務,所以methodB就加入當前事務。
(2)PROPAGATION_SUPPORTS 如果存在一個事務,支持當前事務。如果沒有事務,則非事務的執行。但是對于事務同步的事務管理器,PROPAGATION_SUPPORTS與不使用事務有少許不同。
Java代碼:
//事務屬性 PROPAGATION_REQUIRED methodA(){ methodB(); } //事務屬性 PROPAGATION_SUPPORTS methodB(){ …… }
單純的調用methodB時,methodB方法是非事務的執行的。當調用methdA時,methodB則加入了methodA的事務中,事務地執行。
(3)PROPAGATION_MANDATORY 如果已經存在一個事務,支持當前事務。如果沒有一個活動的事務,則拋出異常。
Java代碼:
//事務屬性 PROPAGATION_REQUIRED methodA(){ methodB(); } //事務屬性 PROPAGATION_MANDATORY methodB(){ …… }
當多帶帶調用methodB時,因為當前沒有一個活動的事務,則會拋出異常throw new IllegalTransactionStateException("Transaction propagation "mandatory" but no existing transaction found");當調用methodA時,methodB則加入到methodA的事務中,事務地執行。
(4)PROPAGATION_REQUIRES_NEW 總是開啟一個新的事務。如果一個事務已經存在,則將這個存在的事務掛起。
Java代碼:
//事務屬性 PROPAGATION_REQUIRED methodA(){ doSomeThingA(); methodB(); doSomeThingB(); } //事務屬性 PROPAGATION_REQUIRES_NEW methodB(){ …… }
Java代碼:
main(){ methodA(); }
相當于
Java代碼:
main(){ TransactionManager tm = null; try{ //獲得一個JTA事務管理器 tm = getTransactionManager(); tm.begin();//開啟一個新的事務 Transaction ts1 = tm.getTransaction(); doSomeThing(); tm.suspend();//掛起當前事務 try{ tm.begin();//重新開啟第二個事務 Transaction ts2 = tm.getTransaction(); methodB(); ts2.commit();//提交第二個事務 } Catch(RunTimeException ex){ ts2.rollback();//回滾第二個事務 } finally{ //釋放資源 } //methodB執行完后,復恢第一個事務 tm.resume(ts1); doSomeThingB(); ts1.commit();//提交第一個事務 } catch(RunTimeException ex){ ts1.rollback();//回滾第一個事務 } finally{ //釋放資源 } }
在這里,我把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 屬性執行。這是一個嵌套事務,使用JDBC 3.0驅動時,僅僅支持DataSourceTransactionManager作為事務管理器。需要JDBC 驅動的java.sql.Savepoint類。有一些JTA的事務管理器實現可能也提供了同樣的功能。使用PROPAGATION_NESTED,還需要把PlatformTransactionManager的nestedTransactionAllowed屬性設為true;而 nestedTransactionAllowed屬性值默認為false;
Java代碼:
//事務屬性 PROPAGATION_REQUIRED methodA(){ doSomeThingA(); methodB(); doSomeThingB(); } //事務屬性 PROPAGATION_NESTED methodB(){ …… }
如果多帶帶調用methodB方法,則按REQUIRED屬性執行。如果調用methodA方法,相當于下面的效果:
Java代碼:
main(){ Connection con = null; Savepoint savepoint = null; try{ con = getConnection(); con.setAutoCommit(false); doSomeThingA(); savepoint = con2.setSavepoint(); try{ methodB(); }catch(RuntimeException ex){ con.rollback(savepoint); } finally{ //釋放資源 } doSomeThingB(); con.commit(); } catch(RuntimeException ex){ con.rollback(); } finally{ //釋放資源 } }
當methodB方法調用之前,調用setSavepoint方法,保存當前的狀態到savepoint。如果methodB方法調用失敗,則恢復到之前保存的狀態。但是需要注意的是,這時的事務并沒有進行提交,如果后續的代碼(doSomeThingB()方法)調用失敗,則回滾包括methodB方法的所有操作。
嵌套事務一個非常重要的概念就是內層事務依賴于外層事務。外層事務失敗時,會回滾內層事務所做的動作。而內層事務操作失敗并不會引起外層事務的回滾。
PROPAGATION_NESTED 與PROPAGATION_REQUIRES_NEW的區別:它們非常類似,都像一個嵌套事務,如果不存在一個活動的事務,都會開啟一個新的事務。使用 PROPAGATION_REQUIRES_NEW時,內層事務與外層事務就像兩個獨立的事務一樣,一旦內層事務進行了提交后,外層事務不能對其進行回滾。兩個事務互不影響。兩個事務不是一個真正的嵌套事務。同時它需要JTA事務管理器的支持。
使用PROPAGATION_NESTED時,外層事務的回滾可以引起內層事務的回滾。而內層事務的異常并不會導致外層事務的回滾,它是一個真正的嵌套事務。DataSourceTransactionManager使用savepoint支持PROPAGATION_NESTED時,需要JDBC 3.0以上驅動及1.4以上的JDK版本支持。其它的JTA TrasactionManager實現可能有不同的支持方式。
PROPAGATION_REQUIRES_NEW 啟動一個新的, 不依賴于環境的 "內部" 事務. 這個事務將被完全 commited 或 rolled back 而不依賴于外部事務, 它擁有自己的隔離范圍, 自己的鎖, 等等. 當內部事務開始執行時, 外部事務將被掛起, 內務事務結束時, 外部事務將繼續執行。
另一方面, PROPAGATION_NESTED 開始一個 "嵌套的" 事務, 它是已經存在事務的一個真正的子事務. 潛套事務開始執行時, 它將取得一個 savepoint. 如果這個嵌套事務失敗, 我們將回滾到此 savepoint. 潛套事務是外部事務的一部分, 只有外部事務結束后它才會被提交。
由此可見, PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區別在于, PROPAGATION_REQUIRES_NEW 完全是一個新的事務, 而 PROPAGATION_NESTED 則是外部事務的子事務, 如果外部事務 commit, 潛套事務也會被 commit, 這個規則同樣適用于 roll back.
PROPAGATION_REQUIRED應該是我們首先的事務傳播行為。它能夠滿足我們大多數的事務需求。
事務的隔離級別使用@Transactional的Isolation屬性可以指定事務的隔離級別。但事務的隔離級別是由底層的數據庫實現的,并不是由Spring來實現。
ISOLATION_DEFAULT:這是默認的隔離級別,使用數據庫默認的事務隔離級別.下邊的四個與JDBC的隔離級別相對應.ISOLATION_READ_UNCOMMITTED:
這是事務最低的隔離級別,它充許另外一個事務可以看到這個事務未提交的數據。這種隔離級別會產生臟讀,不可重復讀和幻像讀。ISOLATION_READ_COMMITTED:保證一個事務修改的數據提交后才能被另外一個事務讀取。另外一個事務不能讀取該事務未提交的數據。這種事務隔離級別可以避免臟讀出現,但是可能會出現不可重復讀和幻像讀。
ISOLATION_REPEATABLE_READ:這種事務隔離級別可以防止臟讀,不可重復讀。但是可能出現幻像讀。它除了保證一個事務不能讀取另一個事務未提交的數據外,還保證了不可重復讀。
ISOLATION_SERIALIZABLE:
這是花費最高代價但是最可靠的事務隔離級別。事務被處理為順序執行。除了防止臟讀,不可重復讀外,還避免了幻像讀。
臟讀:一事務對數據進行了增刪改,但未提交,另一事務可以讀取到未提交的數據。如果第一個事務這時候回滾了,那么第二個事務就讀到了臟數據。
不可重復讀:一個事務中發生了兩次讀操作,第一次讀操作和第二次操作之間,另外一個事務對數據進行了修改,這時候兩次讀取的數據是不一致的。
幻讀:第一個事務對一定范圍的數據進行批量修改,第二個事務在這個范圍增加一條數據,這時候第一個事務就會丟失對新增數據的修改。
總結:
隔離級別越高,越能保證數據的完整性和一致性,但是對并發性能的影響也越大。
大多數的數據庫默認隔離級別為 Read Commited,比如 SqlServer、Oracle
少數數據庫默認隔離級別為:Repeatable Read 比如: MySQL InnoDB
參考文章如下:
spring事務管理(詳解和實例)
Spring 事務機制詳解
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72548.html
摘要:和事務的關系關系型數據庫某些消息隊列等產品或中間件稱為事務性資源,因為它們本身支持事務,也能夠處理事務。事務的傳播特性,,,,,,強制要求要有一個物理事務。外圍事務不會被內部事務的回滾狀態影響。不支持當前事務。 Spring和事務的關系 關系型數據庫、某些消息隊列等產品或中間件稱為事務性資源,因為它們本身支持事務,也能夠處理事務。 Spring很顯然不是事務性資源,但是它可...
摘要:我自己總結的學習的系統知識點以及面試問題,已經開源,目前已經。目前最新的版本中模塊的組件已經被廢棄掉,同時增加了用于異步響應式處理的組件。每一次請求都會產生一個新的,該僅在當前內有效。顯而易見,這種模式存在很多問題。 我自己總結的Java學習的系統知識點以及面試問題,已經開源,目前已經 41k+ Star。會一直完善下去,歡迎建議和指導,同時也歡迎Star: https://githu...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
摘要:作為面試官,我是如何甄別應聘者的包裝程度語言和等其他語言的對比分析和主從復制的原理詳解和持久化的原理是什么面試中經常被問到的持久化與恢復實現故障恢復自動化詳解哨兵技術查漏補缺最易錯過的技術要點大掃盲意外宕機不難解決,但你真的懂數據恢復嗎每秒 作為面試官,我是如何甄別應聘者的包裝程度Go語言和Java、python等其他語言的對比分析 Redis和MySQL Redis:主從復制的原理詳...
閱讀 848·2021-11-24 10:44
閱讀 2786·2021-11-11 16:54
閱讀 3183·2021-10-08 10:21
閱讀 2087·2021-08-25 09:39
閱讀 2907·2019-08-30 15:56
閱讀 3461·2019-08-30 13:46
閱讀 3497·2019-08-23 18:09
閱讀 2084·2019-08-23 17:05