2023-07-06 06:38:48來源:程序員魚皮
今天給朋友們分享的是我們的嘉賓- 大佬 yes 的關(guān)于 MybatisePlus 的文章,希望能對大家有所幫助。
以下是大佬原文:
(相關(guān)資料圖)
昨天測試說有個(gè) xx 功能用不了,扔給我一個(gè)截圖,說有報(bào)錯(cuò):
報(bào)錯(cuò)信息就是:Transaction rolled back because it has been marked as rollback-only
,很好理解:事務(wù)被回滾了,因?yàn)樗呀?jīng)被標(biāo)記了只能回滾。
我一看巧了,這不就是我之前分析過的面試題嗎!
之前的文章我解釋過:這種錯(cuò)一般發(fā)生在嵌套事務(wù)中,即內(nèi)層事務(wù)出錯(cuò),但是由于是否提交事務(wù)的操作由外層事務(wù)觸發(fā),于是乎內(nèi)層事務(wù)只能做個(gè)標(biāo)記,來設(shè)置當(dāng)前事務(wù)只能回滾。
緊接著它想拋出錯(cuò)誤,但是由于被 try catch 了,于是乎正常執(zhí)行后續(xù)的邏輯,等執(zhí)行到最后,外層要提交事務(wù)了,發(fā)現(xiàn)當(dāng)前事務(wù)已經(jīng)被打了回滾的標(biāo)記,所以提交失敗,報(bào)了上面的錯(cuò)。
這里簡單舉例下會出錯(cuò)的示例代碼:
大致就是下面這個(gè)代碼調(diào)用邏輯,有一個(gè) service 標(biāo)記了 @Transcational,采用默認(rèn)的事務(wù)傳播機(jī)制:
緊接著 UserService#insert 調(diào)用了 addressService#errorInvoker,這個(gè)方法也標(biāo)記了 @Transcational:
這樣一來,只要 addressService#errorInvoker 的調(diào)用發(fā)生報(bào)錯(cuò),那么必然能重現(xiàn)上面的報(bào)錯(cuò)信息。
原理很清晰,我不可能犯這個(gè)錯(cuò)。
我信誓旦旦的對測試說:這肯定是老陳寫的 bug,與我無關(guān)!
老陳瞄了我一眼:老子已經(jīng) 2 個(gè)月沒碰過那個(gè)項(xiàng)目了,你扯犢子呢?
隨后這個(gè)老測試直接把更詳細(xì)的報(bào)錯(cuò)扔了過來,咳咳,涉及公司的類名這里不展示的,反正確實(shí)是我的代碼....
但是我還是覺得很不可思議,這部分邏輯是我新寫的,我壓根就沒有使用嵌套事務(wù)啊!
大致的代碼如下:
@Override@Transactional(rollbackFor=)publicBooleanxxx(xxxdto){list1=.....;try{數(shù)據(jù)庫批量保存list1;}catch(Exceptione){if(einstanceofDuplicateKeyException){//篩選過濾重復(fù)key的數(shù)據(jù)//打標(biāo)發(fā)送數(shù)據(jù)庫批量保存過濾之后的list1;}....}sendToMQ(xxx);list2=.....;try{數(shù)據(jù)庫批量保存list2;}catch(Exceptione){if(einstanceofDuplicateKeyException){//篩選過濾重復(fù)key的數(shù)據(jù)//打標(biāo)發(fā)送數(shù)據(jù)庫批量保存過濾之后的list2;}...}sendToMQ(xxx);;}
這個(gè)接口其實(shí)是一次性接口,用來補(bǔ)數(shù)據(jù)的,線上跑過一次后,后面應(yīng)該不會再使用。
出于保險(xiǎn)原則,兼容上游部分?jǐn)?shù)據(jù)重復(fù)調(diào)用,所以我做了重復(fù)key的處理,剔除重復(fù)的部分,讓不重復(fù)的部分正常保存。
正常情況下不會出現(xiàn)這個(gè)場景,剛好測試環(huán)境測試來回折騰有很多重復(fù)數(shù)據(jù)(其實(shí)我這樣寫也是為了兼容測試,隨便他折騰)
這里的代碼邏輯不復(fù)雜,明面上來看,我并沒有調(diào)用別的 service !也并不存在嵌套事務(wù)的問題,所以我思來想去也看不明白。
于是......
我出門放了個(gè)水,順帶逛了一圈,接著買了杯咖啡,遇事不決,量子力...個(gè)屁,立馬屁顛屁顛的跑回來繼續(xù)看代碼。
回來突然就看 try-catch 不爽。
但是 try 里面就是一個(gè) mybatis-plus 的 IService,批量保存數(shù)據(jù)的操作。
難道它有什么騷操作?點(diǎn)進(jìn)去一看突然發(fā)現(xiàn):
我丟!
喚起了我的記憶,mybatis-plus 為了保證批量保存的事務(wù)性,加了 @Transactional
。
合著我確實(shí)沒想著使用嵌套事務(wù),但是這被迫上了“賊船”??!
這本是好意,但是在我這個(gè)場景有點(diǎn)麻,它完美的復(fù)現(xiàn)了上文提到的那個(gè)錯(cuò)誤使用,在有重復(fù) key 的場景確實(shí)報(bào)錯(cuò)了,但是被外層 try-catch 攔住了拋錯(cuò),不過事務(wù)上已經(jīng)打了失敗的標(biāo)了!
解決辦法其實(shí)很簡單:
然后我找了下,好像也沒有什么參數(shù)可以指定 saveBatch 的事務(wù)傳播機(jī)制。
所以咋辦。。。測試還在催我,沒辦法,只能不用 mybatis-plus 的 saveBatch ,自己通過 mapper 寫個(gè)批量插入了:
一波操作提交代碼重啟服務(wù),讓測試再試試,且輕飄飄的甩一句:這不是我的bug,我被框架坑了。
咳咳,反正我不管,我的代碼沒有bug,這是程序員最后的倔強(qiáng)。
所以在使用三方代碼的情況下還是需要多留個(gè)心眼點(diǎn)點(diǎn)看。
我記得以前還聽說過一個(gè)段子,就是有個(gè)人用了一個(gè)網(wǎng)上的組件,正常情況下都沒事,異常情況下,系統(tǒng)就掛了。
后面一找,那個(gè)組件在個(gè)角落嘎達(dá)寫了 。
最后,歡迎學(xué)編程的朋友們加入魚皮的,和上萬名學(xué)編程的同學(xué)共享知識、交流進(jìn)步,學(xué)習(xí)原創(chuàng)項(xiàng)目并享有答疑指導(dǎo)服務(wù)。
往期推薦
關(guān)鍵詞:
這篇文章仍然是HE向,側(cè)重愛情線。如果有想看BE向的,下次會講側(cè)重權(quán)謀
hello,我是數(shù)字華夏網(wǎng)小楊來為大家解答以上問題,唐國強(qiáng)飾演諸葛亮始
黛美品牌由廣州黛美化妝品有限公司于2005年創(chuàng)立,2014年被思步集團(tuán)收購
有事您說話、佳佳幫您辦。多位群眾向《佳佳幫忙》欄目組反映,井陘縣城