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