手把手带你实战下Spring的七种事务传播行为
本文介绍Spring的七种事务传播行为并通过代码演示下。
一、什么是事务传播行为?
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行。
例如:methodA方法调用methodB方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。
二、事务的7种传播行为
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。事务传播行为是Spring框架独有的事务增强特性。这是Spring为我们提供的强大的工具箱,使用事务传播行为可以为我们的开发工作提供许多便利。
7种事务传播行为如下:
1.PROPAGATION_REQUIRED
如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是Spring默认的事务传播行为。
2.PROPAGATION_SUPPORTS
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
3.PROPAGATION_MANDATORY
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
4.PROPAGATION_REQUIRES_NEW
创建新事务,无论当前存不存在事务,都创建新事务。
5.PROPAGATION_NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
6.PROPAGATION_NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
7.PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
其实这7中我也没看懂,不过不急,咱们接下来直接看效果。
三、7种传播行为实战
演示前先建两个表,用户表和用户角色表,一开始两个表里没有数据。
需要注意下,为了数据更直观,每次执行代码时 先清空下user和user_role表的数据。
user表:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
`sex` int(11) DEFAULT NULL,
`des` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
user_role表:
CREATE TABLE `user_role` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`role_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
1.PROPAGATION_REQUIRED测试
如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是Spring默认的事务传播行为。
场景一:
此场景外围方法没有开启事务。
1.验证方法
两个实现类UserServiceImpl和UserRoleServiceImpl制定事物传播行为propagation=Propagation.REQUIRED,然后在测试方法中同时调用两个方法并在调用结束后抛出异常。
2.主要代码
外层调用方法代码:
/**
* 测试 PROPAGATION_REQUIRED
*
* @Author: java_suisui
*/
@Test
void test_PROPAGATION_REQUIRED() {
// 增加用户表
User user = new User();
user.setName("Java碎碎念");
user.setPassword("123456");
userService.add(user);
// 增加用户角色表
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(200);
userRoleService.add(userRole);
//抛异常
throw new RuntimeException();
}
UserServiceImpl代码:
/**
* 增加用户
*/
@Transactional(propagation = Propagation.REQUIRED)
@Override
public int add(User user) {
return userMapper.add(user);
}
UserRoleServiceImpl代码:
/**
* 增加用户角色
*/
@Transactional(propagation = Propagation.REQUIRED)
@Override
public int add(UserRole userRole) {
return userRoleMapper.add(userRole);
}
3.代码执行后数据库截图
两张表数据都新增成功,截图如下:

4.结果分析
外围方法未开启事务,插入用户表和用户角色表的方法在自己的事务中独立运行,外围方法异常不影响内部插入,所以两条记录都新增成功。
场景二:
此场景外围方法开启事务。
1.主要代码
测试方法代码如下:
/**
* 测试 PROPAGATION_REQUIRED
*
* @Author: java_suisui
*/
@Transactional
@Test
void test_PROPAGATION_REQUIRED() {
// 增加用户表
User user = new User();
user.setName("Java碎碎念");
user.setPassword("123456");
userService.add(user);
// 增加用户角色表
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(200);
userRoleService.add(userRole);
//抛异常
throw new RuntimeException();
}
2.代码执行后数据库截图
两张表数据都为空,截图如下:

3.结果分析
外围方法开启事务,内部方法加入外围方法事务,外围方法回滚,内部方法也要回滚,所以两个记录都插入失败。
结论:以上结果证明在外围方法开启事务的情况下Propagation.REQUIRED修饰的内部方法会加入到外围方法的事务中,所以Propagation.REQUIRED修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
2.PROPAGATION_SUPPORTS测试
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
场景一:
此场景外围方法没有开启事务。
1.验证方法
两个实现类UserServiceImpl和UserRoleServiceImpl制定事物传播行为propagation=Propagation.SUPPORTS,然后在测试方法中同时调用两个方法并在调用结束后抛出异常。
2.主要代码
外层调用方法代码:
/**
* 测试 PROPAGATION_SUPPORTS
*
* @Author: java_suisui
*/
@Test
void test_PROPAGATION_SUPPORTS() {
// 增加用户表
User user = new User();
user.setName("Java碎碎念");
user.setPassword("123456");
userService.add(user);
// 增加用户角色表
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(200);
userRoleService.add(userRole);
//抛异常
throw new RuntimeException();
}
UserServiceImpl代码:
/**
* 增加用户
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public int add(User user) {
return userMapper.add(user);
}
UserRoleServiceImpl代码:
/**
* 增加用户角色
*/
@Transactional(propagation = Propagation.SUPPORTS)
@Override
public int add(UserRole userRole) {
return userRoleMapper.add(userRole);
}
3.代码执行后数据库截图
两张表数据都新增成功,截图如下:

4.结果分析
外围方法未开启事务,插入用户表和用户角色表的方法以非事务的方式独立运行,外围方法异常不影响内部插入,所以两条记录都新增成功。
场景二:
此场景外围方法开启事务。
1.主要代码
test_PROPAGATION_SUPPORTS方法添加注解@Transactional即可。
2.代码执行后数据库截图
两张表数据都为空,截图如下:

3.结果分析
外围方法开启事务,内部方法加入外围方法事务,外围方法回滚,内部方法也要回滚,所以两个记录都插入失败。
结论:以上结果证明在外围方法开启事务的情况下Propagation.SUPPORTS修饰的内部方法会加入到外围方法的事务中,所以Propagation.SUPPORTS修饰的内部方法和外围方法均属于同一事务,只要一个方法回滚,整个事务均回滚。
3.PROPAGATION_MANDATORY测试
支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
通过上面的测试,“支持当前事务,如果当前存在事务,就加入该事务”,这句话已经验证了,外层添加@Transactional注解后两条记录都新增失败,所以这个传播行为只测试下外层没有开始事务的场景。
场景一:
此场景外围方法没有开启事务。
1.验证方法
两个实现类UserServiceImpl和UserRoleServiceImpl制定事物传播行为propagation = Propagation.MANDATORY,主要代码如下。
2.主要代码
外层调用方法代码:
/**
* 测试 PROPAGATION_MANDATORY
*
* @Author: java_suisui
*/
@Test
void test_PROPAGATION_MANDATORY() {
// 增加用户表
User user = new User();
user.setName("Java碎碎念");
user.setPassword("123456");
userService.add(user);
// 增加用户角色表
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(200);
userRoleService.add(userRole);
//抛异常
throw new RuntimeException();
}
UserServiceImpl代码:
/**
* 增加用户
*/
@Transactional(propagation = Propagation.MANDATORY)
@Override
public int add(User user) {
return userMapper.add(user);
}
UserRoleServiceImpl代码:
/**
* 增加用户角色
*/
@Transactional(propagation = Propagation.MANDATORY)
@Override
public int add(UserRole userRole) {
return userRoleMapper.add(userRole);
}
3.代码执行后数据库截图
两张表数据都为空,截图如下:

4.结果分析
运行日志如下,可以发现在调用userService.add()时候已经报错了,所以两个表都没有新增数据,验证了“如果当前不存在事务,就抛出异常”。
at com.example.springboot.mybatisannotation.service.impl.UserServiceImpl$$EnhancerBySpringCGLIB$$50090f18.add(<generated>)
at com.example.springboot.mybatisannotation.SpringBootMybatisAnnotationApplicationTests.test_PROPAGATION_MANDATORY(SpringBootMybatisAnnotationApplicationTests.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
4.PROPAGATION_REQUIRES_NEW测试
创建新事务,无论当前存不存在事务,都创建新事务。
这种情况每次都创建事务,所以我们验证一种情况即可。
场景一:
此场景外围方法开启事务。
1.验证方法
两个实现类UserServiceImpl和UserRoleServiceImpl制定事物传播行为propagation = Propagation.REQUIRES_NEW,主要代码如下。
2.主要代码
外层调用方法代码:
/**
* 测试 REQUIRES_NEW
*
* @Author: java_suisui
*/
@Test
@Transactional
void test_REQUIRES_NEW() {
// 增加用户表
User user = new User();
user.setName("Java碎碎念");
user.setPassword("123456");
userService.add(user);
// 增加用户角色表
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(200);
userRoleService.add(userRole);
//抛异常
throw new RuntimeException();
}
UserServiceImpl代码:
/**
* 增加用户
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public int add(User user) {
return userMapper.add(user);
}
UserRoleServiceImpl代码:
/**
* 增加用户角色
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public int add(UserRole userRole) {
return userRoleMapper.add(userRole);
}
3.代码执行后数据库截图
两张表数据都新增成功,截图如下:

4.结果分析
无论当前存不存在事务,都创建新事务,所以两个数据新增成功。
5.PROPAGATION_NOT_SUPPORTED测试
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
场景一:
此场景外围方法不开启事务。
1.验证方法
两个实现类UserServiceImpl和UserRoleServiceImpl制定事物传播行为propagation = Propagation.NOT_SUPPORTED,主要代码如下。
2.主要代码
外层调用方法代码:
/**
* 测试 PROPAGATION_NOT_SUPPORTED
*
* @Author: java_suisui
*/
@Test
void test_PROPAGATION_NOT_SUPPORTED() {
// 增加用户表
User user = new User();
user.setName("Java碎碎念");
user.setPassword("123456");
userService.add(user);
// 增加用户角色表
UserRole userRole = new UserRole();
userRole.setUserId(user.getId());
userRole.setRoleId(200);
userRoleService.add(userRole);
//抛异常
throw new RuntimeException();
}
UserServiceImpl代码:
/**
* 增加用户
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public int add(User user) {
return userMapper.add(user);
}
UserRoleServiceImpl代码:
/**
* 增加用户角色
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
@Override
public int add(UserRole userRole) {
return userRoleMapper.add(userRole);
}
3.代码执行后数据库截图
两张表数据都新增成功,截图如下:

4.结果分析
以非事务方式执行,所以两个数据新增成功。
场景二:
此场景外围方法开启事务。
1.主要代码
test_PROPAGATION_NOT_SUPPORTED方法添加注解@Transactional即可。
2.代码执行后数据库截图
两张表数据都新增成功,截图如下:

3.结果分析
如果当前存在事务,就把当前事务挂起,相当于以非事务方式执行,所以两个数据新增成功。
6.PROPAGATION_NEVER测试
以非事务方式执行,如果当前存在事务,则抛出异常。
上面已经有类似情况,外层没有事务会以非事务的方式运行,两个表新增成功;有事务则抛出异常,两个表都都没有新增数据。
7.PROPAGATION_NESTED测试
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
上面已经有类似情况,外层没有事务会以REQUIRED属性的方式运行,两个表新增成功;有事务但是用的是一个事务,方法最后抛出了异常导致回滚,两个表都都没有新增数据。
到此Spring的7种事务传播行为已经全部介绍完成了,有问题欢迎留言沟通哦!
完整源码地址: https://github.com/suisui2019/springboot-study
推荐阅读
1.SpringBoot系列-整合Mybatis(注解方式)
2.SpringBoot系列-整合Mybatis(XML配置方式)
3.Java中打印日志,这4点很重要!
4.SpringBoot集成JWT实现权限认证
5.一分钟带你了解JWT认证!
限时领取免费Java相关资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高并发分布式、大数据、机器学习等技术。
关注下方公众号即可免费领取:

手把手带你实战下Spring的七种事务传播行为的更多相关文章
- Spring的七种事务传播机制
概述 当我们调用一个基于Spring的Service接口方法(如UserService#addUser())时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其它的Se ...
- spring事务:事务控制方式,使用AOP控制事务,七种事务传播行为,声明事务,模板对象,模板对象原理分析
知识点梳理 课堂讲义 1)事务回顾 1.1)什么是事务-视频01 事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败. 1.2)事务的作用 事务特征(ACID) 原子 ...
- 一分钟带你了解下Spring Security!
一.什么是Spring Security? Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架,它是用于保护基于Spring的应用程序的实际标准. Spring Secu ...
- Spring五个事务隔离级别和七个事务传播行为
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt216 Spring五个事务隔离级别和七个事务传播行为 1. 脏读 :脏读就是 ...
- mysql数据库隔离级别及其原理、Spring的7种事物传播行为
一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有 ...
- Spring 7种事务传播类型
转载:https://www.cnblogs.com/originate918/p/6226342.html PROPAGATION_REQUIRED及其他6种事务传播行为种类. Spring在Tra ...
- 事务特性,事务的隔离级别以及spring中定义的事务传播行为
.katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...
- spring事物的七种事物传播属性行为及五种隔离级别
首先,说说什么事务(Transaction). 事务,就是一组操作数据库的动作集合.事务是现代数据库理论中的核心概念之一.如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务.当 ...
- Spring的四种事务特性,五种隔离级别,七种传播行为
Spring事务: 什么是事务: 事务逻辑上的一组对数据对操作,组成这些操作的各个逻辑单元,要么一起成功,要么一起失败. 事务特性(4种): 原子性(atomicity):强调事务的不可分割:一致性( ...
随机推荐
- A Deep Neural Network Approach To Speech Bandwidth Expansion
题名:一种用于语音带宽扩展的深度神经网络方法 作者:Kehuang Li:Chin-Hui Lee 2015年出来的 摘要 本文提出了一种基于深度神经网络(DNN)的语音带宽扩展(BWE)方法.利用对 ...
- 爬虫之beautifulsoup篇之一
一个网页的节点太多,一个个的用正则表达式去查找不方便且不灵活.BeautifulSoup将html文档转换成一个属性结构,每个节点都是python对象.这样我们就能针对每个结点进行操作.参考如下代码: ...
- RF中的if判断
关键字run keyword if 格式如下: Run Keyword If 判断条件 其他关键字 ... ELSE IF 判断条件 其他关键字 ... ELSE ...
- sql数据文件导入数据库
1.首先通过xshell连接数据库服务器,执行命令mysql -u root -p 命令,按照提示输入密码.连接上数据库. 2.在连接终端上执行命令create database JD_Model; ...
- 奇淫异巧之 PHP 后门
整理大部分来源信安之路 对于隐蔽来说,有以下几点要素: 1.熟悉环境,模拟环境,适应环境,像一只变色龙一样隐藏 2.清除痕迹,避免运维发现 3.避免后门特征值被 D 盾等工具检测到 姿势 一般过狗思路 ...
- pytest5-使用conftest.py实现多文件共享fixture
一个测试工程下是可以有多个conftest.py的文件,一般在工程根目录放一个conftest.py起到全局作用.在不同的测试子目录也可以放conftest.py,作用范围只在该层级以及以下目录生效. ...
- javascript学习总结之Object.assign()方法详解
最近再写ES6的文章时候发现自己对Object.assign()方法不太了解,之前也没有接触过所以就就查阅了相关的资料,为了自己以后肯能会用到以及对知识进行巩固,所以在这里记录下自己学习的点点滴滴,毕 ...
- Feign服务调用请求方式及参数总结
前言 最近做微服务架构的项目,在用feign来进行服务间的调用.在互调的过程中,难免出现问题,根据错误总结了一下,主要是请求方式的错误和接参数的错误造成的.在此进行一下总结记录.以下通过分为三种情况说 ...
- 每日温度(LeetCode Medium难度算法题)题解
LeetCode 题号739中等难度 每日温度 题目描述: 根据每日 气温 列表,请重新生成一个列表,对应位置的输入是你需要再等待多久温度才会升高超过该日的天数.如果之后都不会升高,请在该位置用 0 ...
- Arduino学习笔记② Arduino语言基础
授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...