手把手带你实战下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):强调事务的不可分割:一致性( ...
随机推荐
- 深入研究BufferedReader底层源码
目录 1 概述 2 BufferedReader源码分析 3 意外发现 1 概述 最近研究JDK IO流这一块源码,发现真的比较简单,而且还有很多意外发现,如果大家对JDK源码感兴趣,不妨从IO流这一 ...
- c++ exercises
1.定义个数组并初始化,然后将数组倒置,并显示倒置前和倒置后的结果. #include <iostream> #include <stdio.h> using namespac ...
- TCP三次握手、四次握手
前言 TCP用于应用程序之间的通信.当应用程序希望通过TCP与另一个应用程序通信时,它会发送一个通信请求.这个请求必须被送到一个确切的地址.在双方“握手”之后,TCP将在两个应用程序之间建立一个全双工 ...
- Java11月18日
动手动脑 实验一: 1. 下边的程序运行结果是什么? 2.你如何解释会得到这样的输出? 3.计算机是不会出错的,之所以得到这样的运行结果也是有原因的那么从这些运行结果中,你能总结出Java的哪些语法特 ...
- Java课程作业--参数求和
一.设计思想: 这个程序是利用了参数进行输入,达到一次可以输入多个值的问题,同时输入数的个数没有限制(参数大于0个,如果为0个,应该输出提示请输入参数).本程序共分为步:1.利用参数行进行输入要加的数 ...
- 【Python秘籍】ASCII码与字符的转换
如何在python中显示ASCII码呢?其实你只需要记住两个函数即可:ord()和 chr(),这两个函数都是python内置的函数,不需要引入任何的包,直接就可以使用. 一.显示ASCII码 显示A ...
- 10.Linux用户权限
1.权限基本概述 1. 什么是权限? 我们可以把它理解为操作系统对用户能够执行的功能所设立的限制,主要用于约束用户能对系统所做的操作,以及内容访问的范围,或者说,权限是指某个特定的用户具有特定的系统资 ...
- ANSI最全介绍linux终端字体改变颜色等
ANSI转义序列 维基百科,自由的百科全书 由于国内不能访问wiki而且国内关于ANSI的介绍都是简短的不能达到,不够完整所以转wiki到此博客,方便国内用户参考,原地址(https://zh.wik ...
- [洛谷P3613]睡觉困难综合症
写码30min,调码3h的题.. 好在最后查出来了 , , n, x, y, z); 改成了 , , n, mark[x], y, z); 然后$40\rightarrow 100$ #include ...
- 身为一个 CS专业的留学生,你还在为堆积如山的编程assignment而发愁吗?
每个人都渴望圆梦,当我们看见梦想在别人身上实现时,总在抱怨幸运之神为何不眷顾自己:其实更多的时候,梦想就在你身边的不远处,只要你迈出正确的步伐. 记得刚入大学时,意气风发.我以全班前几名的优异成绩考入 ...