世上無難事敢朱,只要肯攀登
不同service中的A方法中調(diào)用了B方法位喂。兩個(gè)方法只有一個(gè)用到事務(wù)疗垛,那么程序的最終結(jié)果和代碼的執(zhí)行順序有很大關(guān)系
A方法創(chuàng)建了事務(wù)挠阁,B方法無事務(wù)運(yùn)行
我曾經(jīng)以為在A方法中拋出運(yùn)行時(shí)異常宾肺,A方法事務(wù)被回滾。B方法因?yàn)闆]有納入事務(wù)的管理侵俗,所以對(duì)B方法毫無影響锨用。但是經(jīng)過我的實(shí)踐這個(gè)想法是錯(cuò)誤的,程序的最終結(jié)果其實(shí)和代碼的執(zhí)行順序有很大關(guān)系隘谣。下面準(zhǔn)備實(shí)驗(yàn):
我們給InsertUsers方法加上REQUIRED級(jí)別的傳播行為增拥,給InsertCuser方法加上NOT_SUPPORTED級(jí)別的傳播行為。讓InsertUsers方法使用事務(wù)寻歧,InsertCuser方法無事務(wù)狀態(tài)執(zhí)行掌栅。這下肯定有人問:InsertCuser方法不加事務(wù)注解不就能保證InsertCuser無事務(wù)運(yùn)行了嗎?事實(shí)上因?yàn)閟pring的事務(wù)傳播行為码泛,InsertUsers會(huì)將事務(wù)傳播給InsertCuser猾封,導(dǎo)致兩個(gè)方法都使用InsertUsers上的事務(wù),所以必須要給InsertCuser方法加上NOT_SUPPORTED級(jí)別的傳播行為弟晚。不信可以看看我的這篇文章 http://www.reibang.com/p/bc3cbacf9e70 里面有做對(duì)應(yīng)實(shí)驗(yàn)
@Transactional(propagation = Propagation.REQUIRED)
public void InsertUsers(Users users) {
忘衍。。卿城。
}
@Transactional(propagation=Propagation.NOT_SUPPORTED)
public void InsertCuser(Cuser cuser) {
枚钓。。瑟押。
}
然后分別將下列引發(fā)運(yùn)行時(shí)異常的語句放到InsertUsers方法或InsertCuser方法中搀捷,觀察事務(wù)回滾情況
//運(yùn)行時(shí)異常
int i = 1/0;
情況一:
-
如圖在InsertUsers方法中,將拋出異常的代碼行放到調(diào)用InsertCuser方法之前。程序運(yùn)行至此出現(xiàn)異常后根本不會(huì)再往下執(zhí)行了嫩舟,也就是說InsertCuser方法不會(huì)得到執(zhí)行氢烘!即使InsertCuser方法沒有納入事務(wù)的管理,可以理解為此時(shí)它也是執(zhí)行不成功的~ 查看cuser表數(shù)據(jù)為空驗(yàn)證了這個(gè)結(jié)論
image.png -
再來看拋出異常的代碼行放到調(diào)用InsertCuser方法后家厌,結(jié)果就不同了播玖。此時(shí)InsertCuser方法會(huì)得到執(zhí)行,因?yàn)镮nsertCuser方法沒有納入到事務(wù)管理饭于,它不會(huì)回滾蜀踏。執(zhí)行程序后查看數(shù)據(jù)庫cuser表數(shù)據(jù)多出了一行,users表中沒有數(shù)據(jù)
image.png
情況二:
-
再將除0異常代碼放到?jīng)]有納入事務(wù)管理的InsertCuser方法方法中掰吕,觀察事務(wù)回滾情況
把出現(xiàn)異常代碼行放到數(shù)據(jù)庫操作之前果覆,程序運(yùn)行結(jié)果是 InsertUsers和InsertCuser方法數(shù)據(jù)庫操作均失效。因?yàn)镮nsertUsers中存在事務(wù)殖熟,InsertCuser會(huì)隱式拋出運(yùn)行時(shí)異常到InsertUsers中局待,InsertUsers事務(wù)得到回滾;而InsertCuser自身數(shù)據(jù)庫操作失效的原因是數(shù)據(jù)庫操作語句那行根本沒有得到執(zhí)行菱属。
image.png -
若把異常代碼行放到數(shù)據(jù)庫操作之后钳榨,這次InsertUsers同樣因?yàn)槭聞?wù)遇到異常而回滾,而InsertCuser操作成功執(zhí)行纽门。
image.png
A方法無事務(wù)運(yùn)行重绷,B創(chuàng)建事務(wù)
我們不給InsertUsers方法使用事務(wù)注解,給InsertCuser方法加上REQUIRED級(jí)別的傳播行為膜毁。讓InsertUsers無事務(wù)狀態(tài)執(zhí)行,InsertCuser方法創(chuàng)建事務(wù)愤钾。InsertUsers之所以沒有納入事務(wù)的管理是因?yàn)?code>事務(wù)不會(huì)從被調(diào)用者傳播到調(diào)用者
public void InsertUsers(Users users) {
瘟滨。。能颁。
}
@Transactional(propagation=Propagation.REQUIRED)
public void InsertCuser(Cuser cuser) {
杂瘸。。伙菊。
}
情況一:
-
在InsertUsers方法中败玉,把異常代碼行放到insert語句之前
兩者都不會(huì)插入數(shù)據(jù)成功,都是因?yàn)闆]有執(zhí)行到那里去就報(bào)異常了
image.png -
在InsertUsers方法中镜硕,把異常代碼行放到insert語句之后运翼,調(diào)用InsertCuser方法之前;程序運(yùn)行結(jié)果是InsertUsers插入數(shù)據(jù)成功因?yàn)镮nsertUsers沒有納入事務(wù)的管理兴枯,InsertCuser插入數(shù)據(jù)失敗因?yàn)闆]有執(zhí)行到那里去
image.png -
在InsertUsers方法中血淌,把異常代碼行放到調(diào)用InsertCuser方法之后;此時(shí)程序的運(yùn)行結(jié)果是:InsertUsers插入數(shù)據(jù)成功,因?yàn)镮nsertUsers沒有納入事務(wù)的管理悠夯。InsertCuser插入數(shù)據(jù)也成功癌淮,這里可能很多人不理解,因?yàn)檫@個(gè)
int i =1/0
引發(fā)的運(yùn)行時(shí)異常只在本方法中會(huì)影響回滾沦补,也就是說它無法影響已經(jīng)執(zhí)行完畢提交的InsertCuser方法中的事務(wù)
image.png
情況二:
無論將int i =1/0
放InsertCuser方法塊中的哪里乳蓄,結(jié)果都是一樣的。
InsertUsers方法插入數(shù)據(jù)成功夕膀,因?yàn)樗辉谑聞?wù)之內(nèi)虚倒。InsertCuser方法插入數(shù)據(jù)失敗,因?yàn)閽伋霎惓J聞?wù)回滾
大總結(jié)
A方法調(diào)用B方法店诗;這里的上指的是調(diào)用者裹刮,下指的是被調(diào)用者
- 異常總是向上拋出庞瘸,B方法會(huì)將異常拋出到A方法
- 事務(wù)總是伴隨著異常的拋出而回滾捧弃,而且B方法事務(wù)如果進(jìn)行拋出會(huì)影響到A方法的事務(wù)。
方法中如果將異常拋出給上層調(diào)用者方法擦囊,那么當(dāng)前方法的事務(wù)會(huì)回滾
像除0異常這種運(yùn)行時(shí)異常是隱式拋出的不用管违霞、受檢異常就必須顯式拋出,本方法的事務(wù)才會(huì)回滾 - 事務(wù)總是向下傳播瞬场,A方法中有事務(wù)买鸽,B方法中沒事務(wù)。A方法可能會(huì)將事務(wù)傳播給B方法