本文介绍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的七种事务传播行为的更多相关文章

  1. Spring的七种事务传播机制

    概述 当我们调用一个基于Spring的Service接口方法(如UserService#addUser())时,它将运行于Spring管理的事务环境中,Service接口方法可能会在内部调用其它的Se ...

  2. spring事务:事务控制方式,使用AOP控制事务,七种事务传播行为,声明事务,模板对象,模板对象原理分析

    知识点梳理 课堂讲义 1)事务回顾 1.1)什么是事务-视频01 事务可以看做是一次大的活动,它由不同的小活动组成,这些活动要么全部成功,要么全部失败. 1.2)事务的作用 事务特征(ACID) 原子 ...

  3. 一分钟带你了解下Spring Security!

    一.什么是Spring Security? Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架,它是用于保护基于Spring的应用程序的实际标准. Spring Secu ...

  4. Spring五个事务隔离级别和七个事务传播行为

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt216 Spring五个事务隔离级别和七个事务传播行为 1. 脏读 :脏读就是 ...

  5. mysql数据库隔离级别及其原理、Spring的7种事物传播行为

    一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有 ...

  6. Spring 7种事务传播类型

    转载:https://www.cnblogs.com/originate918/p/6226342.html PROPAGATION_REQUIRED及其他6种事务传播行为种类. Spring在Tra ...

  7. 事务特性,事务的隔离级别以及spring中定义的事务传播行为

    .katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...

  8. spring事物的七种事物传播属性行为及五种隔离级别

    首先,说说什么事务(Transaction). 事务,就是一组操作数据库的动作集合.事务是现代数据库理论中的核心概念之一.如果一组处理步骤或者全部发生或者一步也不执行,我们称该组处理步骤为一个事务.当 ...

  9. Spring的四种事务特性,五种隔离级别,七种传播行为

    Spring事务: 什么是事务: 事务逻辑上的一组对数据对操作,组成这些操作的各个逻辑单元,要么一起成功,要么一起失败. 事务特性(4种): 原子性(atomicity):强调事务的不可分割:一致性( ...

随机推荐

  1. 【TencentOS tiny】深度源码分析(3)——队列

    队列基本概念 队列是一种常用于任务间通信的数据结构,队列可以在任务与任务间.中断和任务间传递消息,实现了任务接收来自其他任务或中断的不固定长度的消息,任务能够从队列里面读取消息,当队列中的消息是空时, ...

  2. 【JZOJ5263】分手是祝愿

    Description 请注意本题的数据范围. Input Output Sample Input 2 2 15 19 3 30 40 20 Sample Output 285 2600 Hint 数 ...

  3. wx.navigateTo、wx.redirectTo、wx.reLaunch、wx.switchTab和wx.navigateBack的区别

    wx.navigateTo.wx.redirectTo.wx.reLaunch.wx.switchTab和wx.navigateBack有什么区别呢? **wx.navigateTo:** 用于保留当 ...

  4. 云上的芯脏病:奇怪的阿里云 RDS 数据库突发 CPU 近 100% 问题

    最近遇到了奇怪的阿里云 RDS 数据库突发 CPU 近 100% 问题,遇到了3次. 第一次是10月12日(周六)凌晨 3:24 负载极低的时候开始出现,早上发现后进行了主备切换,恢复了正常. 第二次 ...

  5. Redis实现分布式文件夹锁

    缘起 最近做一个项目,类似某度云盘,另外附加定制功能,本人负责云盘相关功能实现,这个项目跟云盘不同的是,以项目为分配权限的单位,同一个项目及子目录所有有权限的用户可以同时操作所有文件,这样就很容易出现 ...

  6. LeetCode 300. Longest Increasing Subsequence最长上升子序列 (C++/Java)

    题目: Given an unsorted array of integers, find the length of longest increasing subsequence. Example: ...

  7. Web安全之爆破中的验证码识别~

    写爆破靶场的时候发现对于爆破有验证码的有点意思~这里简单总结下我们爆破有验证码的场景中几种有效的方法~~~ 0x01 使用现成工具 这里有pkav团队的神器PKAV HTTP Fuzzer 1.5.6 ...

  8. MySQL make_set()的用法

    MAKE_SET(bits,str1,str2,…)返回一个设定值(含子字符串分隔字符串","字符),在设置位的相应位的字符串.str1对应于位0,str2到第1位,依此类推.在s ...

  9. php 加入 unless 语法

    1. php 的版本 :PHP 7.3.0-dev (cli) (built: Mar 18 2018 00:28:55) ( NTS ) 2. unless 语法结构: unless($cond){ ...

  10. [LUOGU1272] 重建道路 - 树形背包

    题目描述 一场可怕的地震后,人们用N个牲口棚(1≤N≤150,编号1..N)重建了农夫John的牧场.由于人们没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是惟一的.因此,牧场运输系 ...