溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

發(fā)布時(shí)間:2021-11-18 16:43:05 來源:億速云 閱讀:117 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

ACID,事務(wù)內(nèi)的一組操作具有 原子性 、一致性、隔離性、持久性。

  • Atomicity(原子性):一個(gè)事務(wù)(transaction)中的所有操作,要么全部完成,要么全部不完成,不會結(jié)束在中間某個(gè)環(huán)節(jié)。事務(wù)在執(zhí)行過程中發(fā)生錯(cuò)誤,會被恢復(fù)(Rollback)到事務(wù)開始前的狀態(tài),就像這個(gè)事務(wù)從來沒有執(zhí)行過一樣。

  • Consistency(一致性):在事務(wù)開始之前和事務(wù)結(jié)束以后,數(shù)據(jù)庫的完整性沒有被破壞。這表示寫入的資料必須完全符合所有的預(yù)設(shè)規(guī)則,這包含資料的精確度、串聯(lián)性以及后續(xù)數(shù)據(jù)庫可以自發(fā)性地完成預(yù)定的工作。

  • Isolation(隔離性):數(shù)據(jù)庫允許多個(gè)并發(fā)事務(wù)同時(shí)對其數(shù)據(jù)進(jìn)行讀寫和修改的能力,隔離性可以防止多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)由于交叉執(zhí)行而導(dǎo)致數(shù)據(jù)的不一致。事務(wù)隔離分為不同級別,包括讀未提交(Read uncommitted)、讀提交(read committed)、可重復(fù)讀(repeatable read)和串行化(Serializable)。

  • Durability(持久性):事務(wù)處理結(jié)束后,對數(shù)據(jù)的修改就是永久的,即便系統(tǒng)故障也不會丟失。

為什么要使用事務(wù)?

就是一組操作中,存在著多個(gè)更新修改操作,并且要滿足事務(wù)的相關(guān)要求,所以就需要使用到事務(wù)。最常見的例子就是銀行兩個(gè)賬戶間的轉(zhuǎn)賬,包含的A扣款 、B到賬等多個(gè)操作,這些個(gè)操作需要具備事務(wù)的特性。比如說,要么A成功扣款,B也成功到賬;不能出現(xiàn)A扣款了,B沒到賬(原子性);也不能出現(xiàn)現(xiàn)在AB都處理成功了,后續(xù)又出現(xiàn)A賬戶的錢又增多了(持久性);也不能出現(xiàn)A賬號初始余額充足,兩個(gè)并發(fā)處理,導(dǎo)致出現(xiàn)余額為負(fù)的情況(隔離性)。

如何使用事務(wù)?

在spring中可以使用聲明性的注解事務(wù),即在有需要使用的方法、類上,用@Transactional

修飾即可。修飾的方法、類就是這個(gè)事務(wù)的包裹區(qū)域。出現(xiàn)了對應(yīng)的異常就會在AOP中觸發(fā)回滾。

默認(rèn)的回滾是錯(cuò)誤與運(yùn)行異常,不包括檢驗(yàn)異常。

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

rollbackFor參數(shù)支持用戶自行設(shè)置,例如可定義異常跟運(yùn)行異常,如下所示;也支持自定義異常類

@Transactional(rollbackFor = { Exception.class, RuntimeException.class })

默認(rèn)的事務(wù)傳播機(jī)制是Propagation.REQUIRED

事務(wù)的傳播本質(zhì)確定好事務(wù)的限制區(qū)域,即哪些代碼是受到事務(wù)保護(hù)的,出現(xiàn)異??梢曰貪L。

細(xì)節(jié)點(diǎn):

  • 代碼出現(xiàn)事務(wù)配置的異常,在事務(wù)內(nèi)的會自動(dòng)回滾;如果在對應(yīng)的方法體內(nèi)使用了try catch捕獲異常,異常沒有拋出去,那就不會回滾,需要手動(dòng)回滾了。在catch語句中增加手動(dòng)回滾的TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();語句

  • public 方法的事務(wù)才生效

事務(wù)的傳播帶來的幾種結(jié)果

  • 外層沒有事務(wù)的話,內(nèi)在的子方法,沒有的就沒有;有事務(wù)的就會有事務(wù),有事務(wù)的效果形同Propagation.REQUIRES_NEW,互相獨(dú)立,每個(gè)都是一個(gè)不同的新事務(wù)

  • 外層有事務(wù)的話,那這個(gè)整個(gè)方法都在一個(gè)事務(wù)的區(qū)域范圍內(nèi),內(nèi)外任何一處回滾,都是整個(gè)回滾。但是Propagation.NESTED修飾的內(nèi)部方法,可以單獨(dú)回滾掉自己這個(gè)內(nèi)部方法,作為一個(gè)嵌入子事務(wù)所具有的獨(dú)特性。

  • 外層有還是沒有事務(wù),Propagation.REQUIRES_NEW修飾的方法都是作為一個(gè)獨(dú)立的事務(wù),自己獨(dú)立控制回滾與提交,與外層事務(wù)無關(guān)聯(lián)。

此處舉例的事務(wù),指的是 默認(rèn)值為 Propagation.REQUIRED的傳播行為,以及Propagation.NESTED的傳播行為。

兩個(gè)特例

  • 同一個(gè)類中有A、B兩個(gè)方法,A調(diào)用B方法。A沒事務(wù),B有事務(wù),B有異常時(shí),回滾失敗


A沒事務(wù)A有事務(wù)
B沒事務(wù)沒有事務(wù)的效果 不分析事務(wù)生效,B的異常,可以讓整個(gè)A回滾
B有事務(wù)事務(wù)失效事務(wù)生效,同上 不分析

若是A/B在同一個(gè)類中,A方法有事務(wù),B方法沒有事務(wù),這個(gè)時(shí)候事務(wù)會生效,原因是異常傳導(dǎo)到了A方法中;

A方法沒事務(wù),B方法有事務(wù),A調(diào)用B方法。若是A/B在同一個(gè)類中,B方法事務(wù)失效。A/B在不同的類,B方法有事務(wù)效果。

原因分析:這是動(dòng)態(tài)代理導(dǎo)致的,當(dāng)要執(zhí)行B方法的回滾時(shí),此時(shí)A調(diào)用的B方法,不是動(dòng)態(tài)代理的那個(gè)類,無法進(jìn)行回滾。

  • A方法循環(huán)調(diào)用B方法,A方法有事務(wù),B方法啟用新事務(wù),B方法處理成功一條提交一條的數(shù)據(jù);B方法遇到異常,有異常的那條回滾,不影響之前處理成功提交的數(shù)據(jù)。

從之前的推斷來看,Propagation.REQUIRES_NEW修飾的內(nèi)部方法獨(dú)立一個(gè)新事務(wù),跟外層沒有關(guān)系,其實(shí)是兩個(gè)事務(wù)了,外層事務(wù)回滾內(nèi)存的也不會回滾;內(nèi)層回滾也不影響外層事務(wù)。

但是實(shí)際結(jié)果還是有點(diǎn)不太一樣,若是A/B在不同類中,可以達(dá)到這個(gè)效果;同一個(gè)類的話,就會回滾失敗。跟上面AB方法調(diào)用的結(jié)果類似。

究其原因,還是由于使用了動(dòng)態(tài)代理來進(jìn)行事務(wù)AOP的,此時(shí)的B方法一旦觸發(fā)回滾就是事務(wù)回滾異常了。那么要想一個(gè)類中兩個(gè)方法間調(diào)用達(dá)到部分提交的效果,需要使用ApplicationContext 上下文對象獲取當(dāng)前類對象,再進(jìn)行調(diào)用;

// 使用 ApplicationContext 上下文對象獲取該對象;
@Autowired
private ApplicationContext applicationContext; 
CurrentClass classService = applicationContext.getBean(CurrentClass.class); 
//再用這個(gè)對象去調(diào)用同類的其他方法
classService.b();

總結(jié): 事務(wù)的實(shí)現(xiàn)依賴于動(dòng)態(tài)代理,因此在同一個(gè)類中使用了類的其他方法時(shí),就需要額外注意了,只有使用動(dòng)態(tài)代理的對象去調(diào)用方法時(shí),才會有事務(wù)回滾的操作。

事務(wù)傳播屬性propagation

propagation 代表事務(wù)的傳播行為,默認(rèn)值為 Propagation.REQUIRED,總共的屬性信息如下:

  • Propagation.REQUIRED:如果當(dāng)前存在事務(wù),則加入該事務(wù),如果當(dāng)前不存在事務(wù),則創(chuàng)建一個(gè)新的事務(wù)。(默認(rèn)傳播行為,一定會有一個(gè)事務(wù))

( 也就是說如果A方法和B方法都添加了注解,在默認(rèn)傳播模式下,A方法內(nèi)部調(diào)用B方法,會把兩個(gè)方法的事務(wù)合并為一個(gè)事務(wù) )

  • Propagation.SUPPORTS:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則以非事務(wù)的方式繼續(xù)運(yùn)行。(以當(dāng)前是否有事務(wù)為標(biāo)準(zhǔn),可以有事務(wù),也可以沒有事務(wù))

  • Propagation.MANDATORY:如果當(dāng)前存在事務(wù),則加入該事務(wù);如果當(dāng)前不存在事務(wù),則拋出異常。(要求當(dāng)前有事務(wù),就能運(yùn)行;沒有就會異常)

  • Propagation.REQUIRES_NEW:重新創(chuàng)建一個(gè)新的事務(wù),如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。

( 當(dāng)類A中的 a 方法用默認(rèn)Propagation.REQUIRED模式,類B中的 b方法加上采用 Propagation.REQUIRES_NEW模式,然后在 a 方法中調(diào)用 b方法操作數(shù)據(jù)庫,然而 a方法拋出異常后,b方法并沒有進(jìn)行回滾,因?yàn)镻ropagation.REQUIRES_NEW會暫停 a方法的事務(wù) )

  • Propagation.NOT_SUPPORTED:以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),暫停當(dāng)前的事務(wù)。(以非事務(wù)的方式運(yùn)行,當(dāng)前有不報(bào)錯(cuò))

  • Propagation.NEVER:以非事務(wù)的方式運(yùn)行,如果當(dāng)前存在事務(wù),則拋出異常。

  • Propagation.NESTED :如果當(dāng)前存在事務(wù),則嵌套事務(wù)內(nèi)執(zhí)行,如果不存在事務(wù)和 Propagation.REQUIRED 效果一樣。

( 也就是說如果A方法和B方法都添加了注解,在A默認(rèn)傳播模式下,B方法加上采用 Propagation.NESTED模式,A方法內(nèi)部調(diào)用B方法,A回滾,B也會回滾;但B回滾,A不會回滾

數(shù)據(jù)庫隔離級別

事務(wù)的隔離級別依賴于數(shù)據(jù)庫的隔離級別,mysql的默認(rèn)隔離級別是可重復(fù)讀(repeatable read),對應(yīng)的效果是在一個(gè)事務(wù)內(nèi)重復(fù)讀取一個(gè)表中的數(shù)據(jù),一直會是一樣的,并不會讀取到那些個(gè)此事務(wù)范圍內(nèi)其他事務(wù) 未提交(臟讀)、已提交(不可重復(fù)讀)的修改記錄。

在對數(shù)據(jù)進(jìn)行測試隔離級別時(shí),需要先對數(shù)據(jù)庫進(jìn)行一系列的設(shè)置,包括關(guān)閉自動(dòng)提交、查看當(dāng)前的隔離級別,相關(guān)命令如下所示:

//由于變量autocommit分會話系統(tǒng)變量與全局系統(tǒng)變量, Value的值為ON,表示autocommit開啟。
OFF表示autocommit關(guān)閉。
show session variables like 'autocommit';
show global variables like 'autocommit';
 
//關(guān)閉當(dāng)前會話的自動(dòng)提交
set session autocommit=0;
 
//開啟一個(gè)事務(wù)
start transaction; 
begin;   
 
//回滾
rollback;    
//提交事務(wù)
commot;
//查看當(dāng)前的隔離級別   查看全局、當(dāng)前會話的隔離級別
select @@tx_isolation;
SELECT @@global.tx_isolation, @@session.tx_isolation;
 
//設(shè)置當(dāng)前會話的隔離級別為read uncommitted級別:
set session transaction isolation level read uncommitted;
 
//設(shè)置當(dāng)前會話的隔離級別為read committed級別:
set session transaction isolation level read committed;
 
//設(shè)置當(dāng)前會話的隔離級別為repeatable read級別:
set session transaction isolation level repeatable read;
 
//設(shè)置當(dāng)前會話的隔離級別為serializable級別:
set session transaction isolation level serializable;
 
//展示連接id
select connection_id();
 
//數(shù)據(jù)庫超時(shí)設(shè)置查詢
show session variables like '%timeout';

事務(wù)的隔離級別總共分為:未提交讀(read uncommitted)、已提交讀(read committed)、可重復(fù)讀(repeatable read)、串行化(serializable)。

下面將對這四種一一展開說明:

1、未提交讀(會有臟讀的現(xiàn)象)

A事務(wù)已執(zhí)行,但未提交;B事務(wù)查詢到A事務(wù)的更新后數(shù)據(jù);A事務(wù)回滾;那么之前讀取到的A事務(wù)為提交的數(shù)據(jù)就是臟數(shù)據(jù)了。最低的隔離級別,很少會使用到。

---臟讀,讀取到了未提交的數(shù)據(jù)(新增、修改和刪除); 除此之外,還有會不可重復(fù)讀、幻讀的現(xiàn)象。

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

事務(wù)1設(shè)置如上所示,隔離級別為讀未提交,關(guān)閉了自動(dòng)提交。此時(shí)開始一個(gè)事務(wù),看到的有兩條數(shù)據(jù);

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

再打開一個(gè)窗口,關(guān)閉自動(dòng)提交,然后進(jìn)行新增改的操作

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

最后的結(jié)果如上所示,事務(wù)1讀取到了另一事務(wù)未提交時(shí)的新增、修改跟刪除的數(shù)據(jù)。

2、已提交讀

(會有不能重復(fù)讀的現(xiàn)象,因?yàn)槊看巫x取都是讀最新的,那就可能前后兩次會有差異了)

會讀取這一段時(shí)間內(nèi)其他事務(wù)對這些數(shù)據(jù)的變更操作,A事務(wù)執(zhí)行更新;B事務(wù)查詢;A事務(wù)又執(zhí)行更新;B事務(wù)再次查詢時(shí),B事務(wù)前后兩次查詢到的數(shù)據(jù)不一致;例如事務(wù)B要更新狀態(tài),因此先進(jìn)行一次查詢,此時(shí)狀態(tài)為1,一系列操作后,馬上就要更新了,此時(shí)再次查詢,第二次查詢出來的狀態(tài)變成了2。

---不可重復(fù)讀,一般指的是刪除、更新、新增;還會有幻讀的現(xiàn)象

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

不可重復(fù)讀的結(jié)果就是事務(wù)1能夠讀取到另外事務(wù)的 新增、修改、刪除操作,與臟讀的區(qū)別在于,一個(gè)是提交后才能讀取到,一個(gè)是未提交的實(shí)時(shí)操作就能讀取到。

3、可重復(fù)讀 (有可能覆蓋掉其他事務(wù)的操作)

可重復(fù)讀是mysql數(shù)據(jù)的默認(rèn)隔離級別,也是使用的較多的一種隔離級別,下面重點(diǎn)對其分析分析。

A事務(wù)無論執(zhí)行多少次,只要不提交,在這事務(wù)內(nèi)同一個(gè)SQL的查詢值永遠(yuǎn)都不變;可以理解成A事務(wù)內(nèi)的所有查詢都是 查詢A事務(wù)開始時(shí)那一瞬間的數(shù)據(jù)快照;

幻讀: 由于互相隔離,以及可重復(fù)讀的特性,另一個(gè)事務(wù)也同時(shí)在處理同一數(shù)據(jù)的話,就會有一種空幻的現(xiàn)象,好像少了點(diǎn)什么。例如,兩個(gè)事務(wù)都帶id去插入同一數(shù)據(jù),那么后插入的數(shù)據(jù)會加鎖執(zhí)行失?。硪皇聞?wù)未提交)或者主鍵沖突(另一事務(wù)已提交),而插入失敗后再去查詢,又會發(fā)現(xiàn)并沒有找到重復(fù)那條數(shù)據(jù)的,就會有種讀到了空白的感覺,少讀取到了內(nèi)容。 幻讀不僅是插入,更新、刪除也會有這樣的現(xiàn)象的。

現(xiàn)實(shí)的一個(gè)例子,就是離銀行還款日期之前,A去查看賬單表,獲取到了此次的賬單數(shù)據(jù),求得了總和,根據(jù)賬單綜合就將賬單還清了,并且還再次查詢,顯示已經(jīng)還清了。此時(shí),A將本次的查詢,還款操作提交到數(shù)據(jù)庫,在開開心心下班前,突然心血來潮再次進(jìn)行了查詢賬單操作,突然多了幾條消費(fèi)記錄了,需要再次還款。A就感覺 提交事務(wù)前的查詢有點(diǎn)幻讀了,少了幾條數(shù)據(jù)。

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

事務(wù)1在另一個(gè)事務(wù)提交后,再對同樣的數(shù)據(jù)做修改 刪除 新增操作。

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

---幻讀,一般值的是新增;就是明明查詢不到這條數(shù)據(jù),去新增時(shí)會報(bào)錯(cuò)。

其實(shí)更新、刪除也會有的,例如更新同一條用id+status去更新是,后提交的會更新失敗,這一特性也可用來加鎖,即CAS來更新數(shù)據(jù),這樣后操作的肯定就不會覆蓋前面的數(shù)據(jù)了。

已經(jīng)被刪除的數(shù)據(jù),此時(shí)去更新,也不會生效了,在這個(gè)事務(wù)內(nèi)再次查詢還是刪除前的那個(gè)數(shù)據(jù)快照。

基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些

如果更新數(shù)據(jù)時(shí)只用id,存在并發(fā)修改的情況,那么后提交的必定覆蓋之前事務(wù)的更新操作。比如本來數(shù)據(jù)的狀態(tài)是1,事務(wù)2將數(shù)據(jù)狀態(tài)有1->2,而事務(wù)1看的的狀態(tài)還是1,事務(wù)1直接使用id更新的話,將數(shù)據(jù)的狀態(tài)變成了3。事務(wù)1以為是1->3 ,其實(shí)是由 2->3,中間的狀態(tài)2直接就被覆蓋了。因此高并發(fā)的更新,需要慎重。

幻讀總結(jié): 很多對幻讀的解釋是,一個(gè)事務(wù)在查詢的同時(shí),另一個(gè)事務(wù)插入了數(shù)據(jù),然后前一個(gè)事務(wù)再次查詢就會發(fā)現(xiàn)多了幾條數(shù)據(jù),這個(gè)現(xiàn)象是不存在的,如果出現(xiàn)了,那說明當(dāng)前的隔離級別是讀已提交了。

可重復(fù)讀中的就是說同一個(gè)事務(wù)了多次查詢返回的數(shù)據(jù)肯定是一樣的,這是毋庸置疑的,這也是與讀已提交的區(qū)別。因此,只有在當(dāng)前事務(wù)提交后,再次查詢才會刷新到另一事務(wù)的改變。

那么我理解的幻讀就是,在另一事務(wù)新增數(shù)據(jù)并提交后,此時(shí)的事務(wù)去新增同樣一條數(shù)據(jù),會報(bào)錯(cuò)的,而此時(shí)再去查詢又是查無數(shù)據(jù),這種現(xiàn)象才是幻讀。

更新數(shù)據(jù)層面就是,事務(wù)2已經(jīng)將數(shù)據(jù)的狀態(tài)改變提交了,事務(wù)1用舊的狀態(tài)作為條件去更新,影響行數(shù)會是0,這也是一種幻讀。 更新已經(jīng)被刪除的數(shù)據(jù),也是影響行數(shù)為0。

數(shù)據(jù)庫最終的執(zhí)行還是串行的,只是在前置的一些操作可以并發(fā),最終更新到數(shù)據(jù)庫,只能是有一條成功,由于一些規(guī)則的設(shè)置,就會出現(xiàn)上述的現(xiàn)象了。

4、串行化(沒有并發(fā)操作)

串行化是最高的隔離級別,即事務(wù)排隊(duì)串行執(zhí)行了,沒有了并發(fā)操作,也不會發(fā)生上述所說的臟讀、不可重復(fù)讀、幻讀的現(xiàn)象,這個(gè)的使用場景不多,理解起來也較為的簡單。

總結(jié): 數(shù)據(jù)庫的隔離級別就是一個(gè)事務(wù)內(nèi),對于另一事務(wù)的并發(fā)操作會有怎么樣的效果;

  • 另一事務(wù)操作時(shí)就能看到修改后的數(shù)據(jù),就是讀未提交

  • 另一事務(wù)操作并提交后 能看到修改后的數(shù)據(jù),就是讀已提交

  • 另一事務(wù)操作提交后,當(dāng)前事務(wù)依舊看不到相應(yīng)的修改,事務(wù)開始什么數(shù)據(jù),事務(wù)結(jié)束也是讀取到同樣的數(shù)據(jù),就是可重復(fù)讀

所有的事務(wù)都排隊(duì)依次執(zhí)行了,一次只能有一個(gè)進(jìn)行修改,沒有了并行,就是串行化

Spring事務(wù)隔離級別比數(shù)據(jù)庫事務(wù)隔離級別多一個(gè)default

除了上述的四個(gè)隔離級別,多出來 DEFAULT (默認(rèn))這是一個(gè)PlatfromTransactionManager默認(rèn)的隔離級別,即使用數(shù)據(jù)庫默認(rèn)的事務(wù)隔離級別。另外四個(gè)與JDBC的隔離級別相對應(yīng),可以顯性去指定其隔離級別。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持億速云。

“基于Spring中的事務(wù)@Transactional知識點(diǎn)有哪些”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI