javascript
spring手动控制事务开启_“上帝视角”图解Spring事务的传播机制原理
轉(zhuǎn)載:https://mp.weixin.qq.com/s/odP1DKgRtXsCcAKxwGahug
數(shù)據(jù)庫(kù)事務(wù)的“抓手”
數(shù)據(jù)庫(kù)的事務(wù)功能已經(jīng)由數(shù)據(jù)庫(kù)自身實(shí)現(xiàn),它留給用戶的就是三個(gè)指令:開啟事務(wù)、提交事務(wù)和回滾事務(wù)。
開啟事務(wù)一般是start transaction或begin transaction。提交事務(wù)通常都是commit?;貪L事務(wù)通常都是rollback。
數(shù)據(jù)庫(kù)通常都有自動(dòng)開啟事務(wù)和自動(dòng)提交事務(wù)的開關(guān),打開后,事務(wù)就會(huì)自動(dòng)的開啟和提交或回滾。這樣用戶就無感知了。JDBC的事務(wù)“抓手”
JDBC實(shí)現(xiàn)了訪問數(shù)據(jù)庫(kù)的協(xié)議,可以認(rèn)為是對(duì)一些指令的封裝,當(dāng)然也包括這些事務(wù)指令。它們都被做到了java.sql.Connection這個(gè)接口里。
它代表到數(shù)據(jù)庫(kù)的一個(gè)連接,當(dāng)這個(gè)連接建立好后,默認(rèn)事務(wù)就已經(jīng)打開了,而且默認(rèn)情況下執(zhí)行完一個(gè)SQL語(yǔ)句后事務(wù)也是自動(dòng)提交的。
可以通過下面這個(gè)API進(jìn)行檢測(cè):
boolean getAutoCommit();
通常情況下,我們都不希望事務(wù)是一句一提交,而是要執(zhí)行若干個(gè)SQL語(yǔ)句后一次性提交,此時(shí)我們就需要改變這種自動(dòng)提交的行為。
可以通過下面這個(gè)API進(jìn)行設(shè)置:
void setAutoCommit(boolean autoCommit);
當(dāng)我們禁用掉自動(dòng)提交之后,一定要記得自己手動(dòng)提交事務(wù),否則結(jié)果可想而知。當(dāng)然,需要回滾的時(shí)候也要記得手動(dòng)回滾。
可以通過下面這個(gè)API提交事務(wù):
void commit();
可以通過下面這個(gè)API回滾事務(wù):
void rollback();
這樣我們就可以在Java代碼級(jí)別來控制事務(wù)的行為了。同時(shí)我們也應(yīng)該認(rèn)識(shí)到,在Java代碼級(jí)別事務(wù)是和Connecton的實(shí)例對(duì)象綁在一起的。
換句話說,只有在同一個(gè)Connection對(duì)象上執(zhí)行的SQL語(yǔ)句才會(huì)在同一個(gè)事務(wù)里。在不同的Connection對(duì)象上執(zhí)行的SQL語(yǔ)句永遠(yuǎn)不會(huì)在同一個(gè)事務(wù)里。
更精確地說,后者構(gòu)成的是分布式事務(wù),前者通常稱為本地事務(wù)。當(dāng)然,分布式事務(wù)有屬于自己的解決方案,只是不能再使用本地事務(wù)了。備注:以上這些其實(shí)都是基本常識(shí),只是現(xiàn)在的ORM框架太牛了,導(dǎo)致很多年輕的碼農(nóng)都沒機(jī)會(huì)再接觸這些了。Spring的事務(wù)“抓手”
Spring通過使用@Transactional注解實(shí)現(xiàn)了聲明式事務(wù),并且可以通過設(shè)置Propagation屬性來影響事務(wù)的傳播特性。
事務(wù)的傳播特性其實(shí)就是指,Service層的若干方法在互相調(diào)用交織在一起的時(shí)候,究竟哪些方法的代碼是在同一個(gè)事務(wù)里執(zhí)行,哪些方法的代碼不是。
通過前面的分析可知,在同一個(gè)事務(wù)里執(zhí)行的方法代碼背后必須使用的是同一個(gè)Connection對(duì)象,當(dāng)事務(wù)切換時(shí),必須要切換背后的Connection對(duì)象為對(duì)應(yīng)的另一個(gè)。
因此,當(dāng)執(zhí)行流程進(jìn)入/退出不同的方法時(shí),Spring根據(jù)方法上注解的傳播特性,在背后對(duì)應(yīng)的進(jìn)行Connection對(duì)象的切換,也包括新建Connection對(duì)象,提交或回滾事務(wù)等。
我們知道,在寫Service層方法或Mapper層方法時(shí),根本接觸不到Connection對(duì)象,所以它更不可能明目張膽的以參數(shù)的方式傳來傳去,只能在背地里暗箱操作。
由于這些互相交織的方法代碼最終都是在同一個(gè)線程里運(yùn)行的,所以借助線程的ThreadLocal來實(shí)現(xiàn)背后的操作是最適合的。
只需在方法調(diào)用的入口/出口來新建/切換Connection對(duì)象,并提交/回滾Connection對(duì)象上的事務(wù)即可。Spring事務(wù)的傳播特性原理
Spring事務(wù)是通過代理來實(shí)現(xiàn)的,通常是通過CGLIB操作字節(jié)碼來生成子類,因?yàn)橐獎(jiǎng)討B(tài)加入開啟/提交事務(wù)的這些代碼。
下面通過一個(gè)例子來說明,有四個(gè)方法及其對(duì)應(yīng)的傳播特性:
方法一,傳播特性為REQUIRED:
void method1();
方法二,傳播特性為REQUIRED:
void method2();
方法三,傳播特性為REQUIRES_NEW:
void method3();
方法四,傳播特性為REQUIRED:
void method4();
假設(shè)它們之間的調(diào)用關(guān)系是,在方法一里依次調(diào)用方法二三四:
void method1() {
method2();
method3();
method4();
}
可以使用下面這個(gè)圖來表示,圖01:
那么經(jīng)過Spring生成代理后,會(huì)重寫每個(gè)方法,并在原來的每個(gè)方法前面開啟事務(wù),方法后面提交/回滾事務(wù)。
等效的偽代碼如下:
beginTx();
void method1() {
beginTx();
method2();
commit/rollbackTx();
beginTx();
method3();
commit/rollbackTx();
beginTx();
method4();
commit/rollbackTx();
}
commit/rollbackTx();
可以使用下面這個(gè)圖來表示,圖02:
其中紅色的向上箭頭對(duì)應(yīng)于beginTx()操作,藍(lán)色的向下箭頭對(duì)應(yīng)于commit/rollbackTx()操作。
下面就來看看具體的執(zhí)行過程:
一、執(zhí)行方法一的開啟事務(wù),圖03:
由于方法一要求有事務(wù),此時(shí)線程中沒有事務(wù),于是就開啟一個(gè)新的事務(wù),即創(chuàng)建一個(gè)新的Connection對(duì)象并綁定到線程的ThreadLocal。
二、進(jìn)入方法一開始執(zhí)行,圖04:
三、執(zhí)行方法二的開啟事務(wù),圖05:
由于方法二要求有事務(wù),此時(shí)線程中已經(jīng)有事務(wù)了,因此直接參與/使用這個(gè)事務(wù)即可。
四、進(jìn)入方法二開始執(zhí)行,圖06:
五、方法二完畢執(zhí)行提交事務(wù),圖07:
由于該事務(wù)并非方法二新建的,它只是參與而已,所以它沒有資格提交事務(wù),因此實(shí)際并不提交事務(wù)。
六、方法二結(jié)束后又回到方法一里執(zhí)行,圖08:
七、執(zhí)行方法三的開啟事務(wù),圖09:
由于方法三要求新的事物,所以新建一個(gè)事務(wù),并把當(dāng)前的現(xiàn)有事務(wù)掛起,使當(dāng)前線程使用新事務(wù)。
即新建一個(gè)Connection對(duì)象,把當(dāng)前現(xiàn)有Connection對(duì)象與線程的ThreadLocal解綁,把新的Connection對(duì)象綁定到線程的ThreadLocal。
那原來的那個(gè)Connection對(duì)象呢?當(dāng)然是存儲(chǔ)起來了,存儲(chǔ)的形式是依附于新的Connection對(duì)象,即和新的對(duì)象關(guān)聯(lián)一下。
八、進(jìn)入方法三開始執(zhí)行,圖10:
可以看到,原來的事務(wù)相當(dāng)于暫存在新的事務(wù)里。注意,這種說法只是一個(gè)形象的比喻。
九、方法三完畢執(zhí)行提交事務(wù),圖11:
由于這個(gè)事務(wù)是個(gè)新建事務(wù),即是方法三創(chuàng)建的而非參與的,所以有權(quán)提交,所以事務(wù)就真的提交了。
十、方法三結(jié)束后又回到方法一里執(zhí)行,圖12:
方法三的事務(wù)完成了,但是它里面暫存了方法一的事務(wù),于是把它重新恢復(fù)到線程里去。
十一、執(zhí)行方法四的開啟事務(wù),圖13:
由于方法四要求有事務(wù),此時(shí)線程中已經(jīng)有事務(wù)了,因此直接參與/使用這個(gè)事務(wù)即可。
十二、進(jìn)入方法四開始執(zhí)行,圖14:
十三、方法四完畢執(zhí)行提交事務(wù),圖15:
由于該事務(wù)并非方法四新建的,它只是參與而已,所以它沒有資格提交事務(wù),因此實(shí)際并不提交事務(wù)。
十四、方法四結(jié)束后又回到方法一里執(zhí)行,圖16:
十五、方法一完畢執(zhí)行提交事務(wù),圖17:
由于這個(gè)事務(wù)是個(gè)新建事務(wù),即是方法一創(chuàng)建的而非參與的,所以有權(quán)提交,所以事務(wù)就真的提交了。
十六、所有事務(wù)完成,只剩一個(gè)空線程,圖18:
方法一的事務(wù)完成了,由于它里面沒有暫存其它事務(wù),所以沒有事務(wù)了,因此最后只剩一個(gè)空線程。說明:這只是傳播特性的實(shí)現(xiàn)原理解說,所以比較簡(jiǎn)單,實(shí)際代碼實(shí)現(xiàn)要考慮很多事情,因此會(huì)復(fù)雜很多。
推薦閱讀:
太好了!某騰花399¥買來史上最全MySQL數(shù)據(jù)庫(kù)視頻教程(2020Java面試必備底層)
Spring源碼實(shí)戰(zhàn)全集,資深架構(gòu)師帶你搞懂Spring源碼底層從入門到入墳
阿里P9架構(gòu)師120分鐘帶你掌握線程池,不在為線程而煩惱_
面試美團(tuán)被JVM慘虐?阿里P9架構(gòu)師用500分鐘把JVM從入門講到實(shí)戰(zhàn)#合集
全B站有史以來最全阿里、騰訊、字節(jié)、美團(tuán)、谷歌算法面試題合集_
總結(jié)
以上是生活随笔為你收集整理的spring手动控制事务开启_“上帝视角”图解Spring事务的传播机制原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 原理 快速邻近匹配_论文推荐 | 陈晓勇
- 下一篇: 用户请求队列化_爬虫架构消息队列应用场景