事务传播行为的失效(异常传递)
眾所周知,事務(wù)的傳播行為一共有7種,7種傳播行為具體有什么特征在這里不再贅述,詳情參考https://zhuanlan.zhihu.com/p/256263914,本文主要對(duì)對(duì)于傳播行為失效進(jìn)行探究,旨在對(duì)于事務(wù)的使用時(shí)對(duì)于細(xì)節(jié)的把控。
在這里引出第一個(gè)問(wèn)題:
1.為什么我用 @Transactional(propagation = Propagation.REQUIRES_NEW)卻得到了意想不到的結(jié)果?
首先,由于spring中的事務(wù)底層采用的是jdk或cglib的動(dòng)態(tài)代理,因此,不能在一個(gè)類(lèi)中一個(gè)方法直接調(diào)用另一個(gè)方法,可以采用動(dòng)態(tài)代理上下文的方式,在一個(gè)類(lèi)中調(diào)用另一個(gè)采用事務(wù)的方法,具體參考https://blog.csdn.net/xlgen157387/article/details/79026285,亦或者在當(dāng)前類(lèi)中注入當(dāng)前類(lèi)的對(duì)象,采用對(duì)象的方式調(diào)用。以下采用兩個(gè)類(lèi)的方式進(jìn)行調(diào)用。
代碼如下:
@Servicepublic class TestTransactional {@Autowiredprivate CustomerMapper customerMapper;@Autowiredprivate TestTransactional2 testTransactional2;@Transactional(propagation = Propagation.REQUIRED)public void methodA() {System.out.println("pre");Customer customer = new Customer();customer.setAddress("zzzzzzzz");customer.setNickname("zzzzzzzz");customer.setUsername("zzzzzzzz");customer.setPassword("zzzzzzzz");customerMapper.insert(customer);testTransactional2.methodB(65);System.out.println("post");}} @Service public class TestTransactional2 {@Autowiredprivate CustomerMapper customerMapper;@Transactional(propagation = Propagation.REQUIRES_NEW)public void methodB(Integer id) {customerMapper.deleteById(id);throw new RuntimeException();} }methodA中事務(wù)采用Propagation.REQUIRED,在methodA調(diào)用methodB,methodB的事務(wù)傳播行為為Propagation.REQUIRES_NEW,按照Propagation.REQUIRES_NEW的特征,方法B會(huì)開(kāi)啟一個(gè)新的事務(wù),方法A的事務(wù)會(huì)被掛起,如果方法B發(fā)生異常,方法B回滾,但是方法A不回滾,但是當(dāng)方法B發(fā)生異常時(shí),方法A也進(jìn)行了回滾。如下:
可以發(fā)現(xiàn)65條數(shù)據(jù)沒(méi)有刪除,也沒(méi)有添加新的數(shù)據(jù),說(shuō)明方法B和方法A都發(fā)生了回滾。這是為什么呢?
這事因?yàn)?#xff0c;方法B執(zhí)行時(shí)拋出了異常,而方法A中并沒(méi)有進(jìn)行捕獲,造成方法A中也出現(xiàn)了異常,造成A的事務(wù)也進(jìn)行了回滾。如果在方法A中加入try{}catch{},那么問(wèn)題就會(huì)迎刃而解。如下:
@Service public class TestTransactional {@Autowiredprivate CustomerMapper customerMapper;@Autowiredprivate TestTransactional2 testTransactional2;@Transactional(propagation = Propagation.REQUIRED)public void methodA() {System.out.println("pre");Customer customer = new Customer();customer.setAddress("zzzzzzzz");customer.setNickname("zzzzzzzz");customer.setUsername("zzzzzzzz");customer.setPassword("zzzzzzzz");customerMapper.insert(customer);try {testTransactional2.methodB(65);} catch (Exception e) {e.printStackTrace();}System.out.println("post");}}可以看出,方法B進(jìn)行了回滾,方法A沒(méi)有進(jìn)行回滾。
2.如果methodB的傳播采用@Transactional(propagation = Propagation.REQUIRED)時(shí),我可以對(duì)異常進(jìn)行捕獲嗎?
但是當(dāng)方法B中事務(wù)傳播采用Propagation.REQUIRED(如果上下文中已經(jīng)存在事務(wù),那么就加入到事務(wù)中執(zhí)行,如果當(dāng)前上下文中不存在事務(wù),則新建事務(wù)執(zhí)行)時(shí),try{}catch{}會(huì)不會(huì)影響事務(wù)的執(zhí)行呢?
@Service public class TestTransactional2 { @Autowired private CustomerMapper customerMapper;@Transactional(propagation = Propagation.REQUIRED) public void methodB(Integer id) {customerMapper.deleteById(id);throw new RuntimeException();} }可以發(fā)現(xiàn),try{}catch{}并沒(méi)有影響Propagation.REQUIRED的特征,因?yàn)?#xff0c;方法B使用的事務(wù)是方法A中的事務(wù),方法A與方法B公用一個(gè)事務(wù),如果方法B發(fā)生了異常,即使方法A中進(jìn)行了捕獲,方法A也會(huì)發(fā)生回滾。所以,try{}catch{}也應(yīng)用于propagation = Propagation.REQUIRED時(shí)。
3.如果方法B中事務(wù)傳播采用Propagation.NOT_SUPPORTED時(shí),我需要進(jìn)行異常捕獲嗎?
Propagation.NOT_SUPPORTED,當(dāng)前級(jí)別的特點(diǎn)就是上下文中存在事務(wù),則掛起事務(wù),執(zhí)行當(dāng)前邏輯,結(jié)束后恢復(fù)上下文的事務(wù)。
@Service public class TestTransactional {@Autowiredprivate CustomerMapper customerMapper;@Autowiredprivate TestTransactional2 testTransactional2;@Transactional(propagation = Propagation.REQUIRED)public void methodA() {System.out.println("pre");Customer customer = new Customer();customer.setAddress("zzzzzzzz");customer.setNickname("zzzzzzzz");customer.setUsername("zzzzzzzz");customer.setPassword("zzzzzzzz");customerMapper.insert(customer);testTransactional2.methodB(65);System.out.println("post");}} @Service public class TestTransactional2 {@Autowiredprivate CustomerMapper customerMapper;@Transactional(propagation = Propagation.NOT_SUPPORTED)public void methodB(Integer id) {customerMapper.deleteById(id);throw new RuntimeException();} }當(dāng)方法A沒(méi)有進(jìn)行異常捕獲時(shí),得到了意想不到的結(jié)果:
方法B中刪除成功,方法A進(jìn)行了回滾,按照Propagation.NOT_SUPPORTED的特性,應(yīng)該是方法B沒(méi)有事務(wù)的執(zhí)行,不會(huì)影響方法A中的事務(wù),但是,結(jié)果顯示,方法B拋出的異常影響了方法A中的事務(wù),因此,方法A中一定要進(jìn)行異常的捕獲。
4.如果方法B中事務(wù)傳播采用Propagation.NESTED時(shí),我需要進(jìn)行異常捕獲嗎?
Propagation.NESTED,嵌套是子事務(wù)套在父事務(wù)中執(zhí)行,子事務(wù)是父事務(wù)的一部分,在進(jìn)入子事務(wù)之前,父事務(wù)建立一個(gè)回滾點(diǎn),叫save point,然后執(zhí)行子事務(wù),這個(gè)子事務(wù)的執(zhí)行也算是父事務(wù)的一部分,然后子事務(wù)執(zhí)行結(jié)束,父事務(wù)繼續(xù)執(zhí)行。
如果子事務(wù)回滾,會(huì)發(fā)生什么?
父事務(wù)會(huì)回滾到進(jìn)入子事務(wù)前建立的save point,然后嘗試其他的事務(wù)或者其他的業(yè)務(wù)邏輯,父事務(wù)之前的操作不會(huì)受到影響,更不會(huì)自動(dòng)回滾。
如果父事務(wù)回滾,會(huì)發(fā)生什么?
父事務(wù)回滾,子事務(wù)也會(huì)跟著回滾!為什么呢,因?yàn)楦甘聞?wù)結(jié)束之前,子事務(wù)是不會(huì)提交的,我們說(shuō)子事務(wù)是父事務(wù)的一部分,正是這個(gè)道理。那么:
@Service public class TestTransactional {@Autowiredprivate CustomerMapper customerMapper;@Autowiredprivate TestTransactional2 testTransactional2;@Transactional(propagation = Propagation.REQUIRED)public void methodA() {System.out.println("pre");Customer customer = new Customer();customer.setAddress("zzzzzzzz");customer.setNickname("zzzzzzzz");customer.setUsername("zzzzzzzz");customer.setPassword("zzzzzzzz");customerMapper.insert(customer);testTransactional2.methodB(65);System.out.println("post");}} @Service public class TestTransactional2 {@Autowiredprivate CustomerMapper customerMapper;@Transactional(propagation = Propagation.NESTED)public void methodB(Integer id) {customerMapper.deleteById(id);throw new RuntimeException();} }當(dāng)方法A中沒(méi)有對(duì)異常進(jìn)行捕獲時(shí),結(jié)果如下:
可以發(fā)現(xiàn),方法B和方法A都進(jìn)行了回滾,按照NESTED特性,應(yīng)該方法A不會(huì)進(jìn)行回滾而是回到了開(kāi)始執(zhí)行方法B的保存的point,但是方法A進(jìn)行了回滾。當(dāng)方法A中對(duì)異常進(jìn)行捕獲時(shí),結(jié)果就會(huì)與預(yù)期一致。
綜上所述:無(wú)論方法B中傳播行為是何種方式,方法A中都要對(duì)異常進(jìn)行捕獲,否則會(huì)發(fā)生出乎意料的結(jié)果,而且這種錯(cuò)誤還很難發(fā)現(xiàn),雖然這是運(yùn)用事務(wù)時(shí)的細(xì)節(jié)問(wèn)題,但是在一些項(xiàng)目中總是這些細(xì)節(jié)造成重大的損失,因此,在我們做項(xiàng)目時(shí)在邏輯嚴(yán)謹(jǐn)?shù)耐瑫r(shí)也要對(duì)一些細(xì)節(jié)進(jìn)行把握,增強(qiáng)代碼的健壯性。
總結(jié)
以上是生活随笔為你收集整理的事务传播行为的失效(异常传递)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: python pop3lib连接网易企业
- 下一篇: 计算机屏幕亮度一般为多少,显示器亮度对比