众所周知,事务的传播行为一共有7种,7种传播行为具体有什么特征在这里不再赘述,详情参考https://zhuanlan.zhihu.com/p/256263914,本文主要对对于传播行为失效进行探究,旨在对于事务的使用时对于细节的把控。

在这里引出第一个问题:

1.为什么我用 @Transactional(propagation = Propagation.REQUIRES_NEW)却得到了意想不到的结果?

首先,由于spring中的事务底层采用的是jdk或cglib的动态代理,因此,不能在一个类中一个方法直接调用另一个方法,可以采用动态代理上下文的方式,在一个类中调用另一个采用事务的方法,具体参考https://blog.csdn.net/xlgen157387/article/details/79026285,亦或者在当前类中注入当前类的对象,采用对象的方式调用。以下采用两个类的方式进行调用。

代码如下:

 @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中事务采用Propagation.REQUIRED,在methodA调用methodB,methodB的事务传播行为为Propagation.REQUIRES_NEW,按照Propagation.REQUIRES_NEW的特征,方法B会开启一个新的事务,方法A的事务会被挂起,如果方法B发生异常,方法B回滚,但是方法A不回滚,但是当方法B发生异常时,方法A也进行了回滚。如下:

可以发现65条数据没有删除,也没有添加新的数据,说明方法B和方法A都发生了回滚。这是为什么呢?

这事因为,方法B执行时抛出了异常,而方法A中并没有进行捕获,造成方法A中也出现了异常,造成A的事务也进行了回滚。如果在方法A中加入try{}catch{},那么问题就会迎刃而解。如下:

@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进行了回滚,方法A没有进行回滚。

2.如果methodB的传播采用@Transactional(propagation = Propagation.REQUIRED)时,我可以对异常进行捕获吗?

但是当方法B中事务传播采用Propagation.REQUIRED(如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行)时,try{}catch{}会不会影响事务的执行呢?

@Service
public class TestTransactional2 {@Autowired
private CustomerMapper customerMapper;@Transactional(propagation = Propagation.REQUIRED)
public void methodB(Integer id) {customerMapper.deleteById(id);throw new RuntimeException();}
}

可以发现,try{}catch{}并没有影响Propagation.REQUIRED的特征,因为,方法B使用的事务是方法A中的事务,方法A与方法B公用一个事务,如果方法B发生了异常,即使方法A中进行了捕获,方法A也会发生回滚。所以,try{}catch{}也应用于propagation = Propagation.REQUIRED时。

3.如果方法B中事务传播采用Propagation.NOT_SUPPORTED时,我需要进行异常捕获吗?

Propagation.NOT_SUPPORTED,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。

@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();}
}

当方法A没有进行异常捕获时,得到了意想不到的结果:

方法B中删除成功,方法A进行了回滚,按照Propagation.NOT_SUPPORTED的特性,应该是方法B没有事务的执行,不会影响方法A中的事务,但是,结果显示,方法B抛出的异常影响了方法A中的事务,因此,方法A中一定要进行异常的捕获。

4.如果方法B中事务传播采用Propagation.NESTED时,我需要进行异常捕获吗?

Propagation.NESTED,嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。

如果子事务回滚,会发生什么?

父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

如果父事务回滚,会发生什么?

父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。那么:

@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();}
}

当方法A中没有对异常进行捕获时,结果如下:

可以发现,方法B和方法A都进行了回滚,按照NESTED特性,应该方法A不会进行回滚而是回到了开始执行方法B的保存的point,但是方法A进行了回滚。当方法A中对异常进行捕获时,结果就会与预期一致。

综上所述:无论方法B中传播行为是何种方式,方法A中都要对异常进行捕获,否则会发生出乎意料的结果,而且这种错误还很难发现,虽然这是运用事务时的细节问题,但是在一些项目中总是这些细节造成重大的损失,因此,在我们做项目时在逻辑严谨的同时也要对一些细节进行把握,增强代码的健壮性。

事务传播行为的失效(异常传递)相关推荐

  1. 事务传播机制/数据库异常解析——2016-8-13分享总结

    一. 事务的传播机制/required 跟 required new 的使用与区别 基础回顾 1.1 事务的隔离级别: ISOLATION_READ_UNCOMMITTED(读未提交) ISOLATI ...

  2. spring 事务传播行为以及失效原因

    今天在查看以前写的代码时,看到了事务的使用,感觉自己对这一块并不是特别清晰,所以就系统的学习了一下.在学习过程中发现很多地方自己以前理解的还是有点不对,所以记录一下学习笔记,希望帮助到大家. 一.事务 ...

  3. 0046 @Transactional注解的几个参数--事务传播控制--事务隔离级别--异常与回滚

    0046 @Transactional注解的几个参数--事务传播控制--事务隔离级别--异常与回滚 参考文章: (1)0046 @Transactional注解的几个参数--事务传播控制--事务隔离级 ...

  4. @Transactional注解的几个参数--事务传播控制--事务隔离级别--异常与回滚

    @Transactianal注解有一些属性,见Spring-Framework-Reference Table17.3 Transactional-settings @Transactional(pr ...

  5. Spring事务与事务传播机制

    目录 1.事务的基本概念 2.Spring事务的实现 3.事务隔离级别 4.事务传播机制 1.事务的基本概念 关于事务的一些基础概念我已经在MYSQL中讲解过了,有不了解的可以移步至此篇文章: MyS ...

  6. JavaEE_Spring_Spring中的事务声明, 事务隔离和事务传播等

    最近面试经常被问到事务的隔离与事务传播等方面的知识,现在留下一篇博文整理了网上几篇博文的资料.记录一下 0.Spring中的事务以及事务声明 这里只讲解Spring中的事务,对于SQL事务的概念以及A ...

  7. Spring的7种事务传播行为类型

    1.PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置. 2.PROPAGATION_SUPPORTS:支持当前事务,如 ...

  8. Spring事务传播机制和隔离级别

    Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非常的绕.如果栽在这上面,就实在是太可惜了. @Transactional(is ...

  9. jdbctemplate 开启事务_浅入浅出 Spring 事务传播实现原理

    本文和大家一起刨析 Spring 事务的相关源码,篇幅较长,代码片段较多,建议使用电脑阅读 本文目标 理解Spring事务管理核心接口 理解Spring事务管理的核心逻辑 理解事务的传播类型及其实现原 ...

最新文章

  1. 开篇第一题:经典中的经典!
  2. Teamviewer连接服务器显示不能够全屏 或 向日葵连接服务器不能够全屏 或 Teamviewer只显示一个640x480的分辨率选项
  3. jquery实战-定宽(二)
  4. (八)企业部分之nginx+tomcat+memcached负载均衡集群搭建
  5. python 2x可以打么_Python打基础一定要吃透这68个内置函数
  6. 轻松搞定 Nginx 配置的好工具!
  7. 数据科学 IPython 笔记本 8.11 多个子图
  8. window server 2008配置FTP服务器550 Access is denied. 问题解决办法
  9. [红外] 求一个数的反码, 并作为高位追加到 这个数的前面
  10. linux重置电池阀值,Thinkpad在linux(ubuntu)下修改电池充电阈值,成功解决Thinkpad在Linux下的电池充电问题...
  11. FFT(快速傅里叶) c语言版
  12. java udp发送速率_项目总结22:Java UDP Socket数据的发送和接收
  13. 解微分方程数值解法(理论部分)
  14. 【DSP】【第二篇】了解C6678和创建工程
  15. 定投的收益率怎么计算
  16. 怎样把mp4视频转换成mov格式电影
  17. 自动化所夏令营数学概念复习
  18. vue尚品汇商城项目-day00【项目介绍:此项目是基于vue2的前台电商项目和后台管理系统】
  19. houdini环境变量服务器文件读不了,Windows下在普通命令行窗口里初始化Houdini环境...
  20. 储种_定活两便_整存整取_and so on

热门文章

  1. java--匿名内部类
  2. **python练习访问*
  3. IP68防尘防水等级
  4. Github+Hexo搭建个人博客(图文详解)
  5. Android 设置应用的底部导航栏(虚拟按键)背景颜色
  6. 安装艾默生流量计后的校准方法
  7. mysqldump导出数据库视图_mysqldump导出视图数据库
  8. 2021-09-03 网安实验-文件修复-Stegano练习之隐写5
  9. 俄罗斯 Yandex 浏览器:比微软 Edge 和谷歌 Chrome 还香
  10. docker保存镜像报错:Cowardly refusing to save to a terminal. Use the -o flag or redirect.