手把手带你实战下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):强调事务的不可分割:一致性( ...
随机推荐
- C语言入门-结构类型
一.声明结构类型 #include <stdio.h> int main(int argc, char const *argv[]) { // 声明结构类型 struct date { i ...
- MyEclipse10 使用JRebel实现热部署
MyEclipse10 使用JRebel实现热部署 Window --Preferences-Tomcat 6.x-JDK-JVM -noverify -javaagent:D:\JRebel\jre ...
- API---文件操作
CreateFile() 介绍: 功能:打开或创建以下对象,并返回可访问的句柄: 控制台,通信资源,目录(只读打开),磁盘驱动器,文件,邮槽,管道. 函数原型:HANDLE CreateFile ( ...
- windows离线安装sublime插件:ctags
网络上一堆安装ctags教程,可都是在线安装. 花了点时间摸索出了离线安装教程. 1. 准备好sublime 和 package control sublime我用的版本是text 2. text 3 ...
- 新手也能看懂的 SpringBoot 异步编程指南
本文已经收录自 springboot-guide : https://github.com/Snailclimb/springboot-guide (Spring Boot 核心知识点整理. 基于 S ...
- opencv实践::对象的提取
问题描述 真实案例,对图像中对象进行提取,获取这样对象,去掉其它干扰和非目标对象. 解决思路 二值分割 + 形态学处理 +横纵比计算 #include <opencv2/opencv.hpp&g ...
- 概念理解:boost::asio::定时器2
多线程同步回调#include <cstdio> #include <iostream> #include <boost/asio.hpp> #include &l ...
- Vue项目多域名跨域
在Vue项目中请求后台数据时,遇到的多域名跨域问题. 直接上代码: assetsSubDirectory: "static", assetsPublicPath: "/& ...
- c语言1博客作业02
c语言1博客作业02 这个作业属于哪个课程 C语言程序设计 这个作业的要求在哪 [作业要求](https://edu.cnblogs.com/campus/zswxy/SE2019-2/homewor ...
- vmware14安装centos7的步骤(图文详解)
一.centos的安装 centos分为桌面版和黑屏版(命令行版):在这里我使用的是命令行版. 这里选择安装程序光盘映像文件,文件就是centos7的iso文件. 虚拟机的名称和位置自行设置; 虚拟机 ...