Spring事务传播行为实战
一、什么是事务传播行为?
事务传播行为(propagation behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何运行。
例如:methodA方法调用methodB方法时,methodB是继续在调用者methodA的事务中运行呢,还是为自己开启一个新事务运行,这就是由methodB的事务传播行为决定的。
二、事务传播行为类型
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。
事务传播行为是Spring框架独有的事务增强特性,这是Spring为我们提供的强大的工具箱,使用事务传播行为可以为我们的开发工作提供许多便利。
两大类
- 支持事务的传播
- 不支持事物的传播
七小种
- REQUIRED:(支持事务)如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务(Spring默认)
- SUPPORTS:(支持事务)如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行
- MANDATORY:(支持事务)如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常
- REQUIRES_NEW:(支持事务)创建新事务,无论当前存不存在事务,都创建新事务
- NOT_SUPPORTED:(不支持事务)如果当前存在事务,就把当前事务挂起
- NEVER:(不支持事务)以非事务方式执行,如果当前存在事务,则抛出异常
- NESTED:(支持事务)如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,就创建一个新事务
三、事务传播行为实战
**说明:**父方法插入表ks_a、子方法插入表ks_b
表结构:
CREATE TABLE `ks_a` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`name` varchar(20) DEFAULT NULL COMMENT '姓名',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='测试A';
CREATE TABLE `ks_b` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键id',
`age` tinyint(4) DEFAULT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='测试B';
1、REQUIRED
1.1、父方法无事务,子方法开启事务,子方法报错
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:ks_a数据插入成功,ks_b数据回滚

1.2、父方法开启事务,子方法开启事务,父方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
throw new RuntimeException("主方法报错");
}
@Transactional(propagation = Propagation.REQUIRED)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
结果:两表数据都回滚了

1.3、父方法开启事务,子方法开启事务,子方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.REQUIRED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:两表数据都回滚了

总结
父方法无事务,子方法开启新事务
父方法有事务,子方法和父方法共用一个事务(无论父、子方法报错,整体回滚)
2、SUPPORTS
2.1、父方法无事务,子方法开启事务,子方法报错
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.SUPPORTS)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:数据都插入成功

2.2、父方法开启事务,子方法开启事务,子方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.SUPPORTS)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:两表数据都回滚了

总结
如果当前不存在事务,就以非事务执行
如果当前存在事务,就加入该事务
3、MANDATORY
3.1、父方法无事务,子方法开启事务,子方法报错
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.MANDATORY)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:org.springframework.transaction.IllegalTransactionStateException: No existing transaction found for transaction marked with propagation ‘mandatory’
3.2、父方法开启事务,子方法开启事务,子方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.MANDATORY)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:两表数据都回滚了

总结
如果当前不存在事务,就抛出异常
如果当前存在事务,就加入该事务
4、REQUIRES_NEW
4.1、父方法无事务,子方法开启事务,子方法都报错
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:ks_a数据插入成功,ks_b数据回滚

4.2、父方法开启事务,子方法开启事务,父方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
throw new RuntimeException("父方法报错");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
结果:ks_a数据回滚,ks_b数据插入成功

总结
无论当前存不存在事务,都创建新事务
5、NOT_SUPPORTED
5.1、父方法无事务,子方法开启事务,子方法报错
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:数据都插入成功

5.2、父方法开启事务,子方法开启事务,子方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:ks_a数据回滚,ks_b数据插入成功

总结
以非事务方式执行,如果当前存在事务,父方法以事务方式执行,子方法以非事务方式执行
6、NEVER
父方法开启事务,子方法开启事务
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
}
@Transactional(propagation = Propagation.NEVER)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
结果:org.springframework.transaction.IllegalTransactionStateException: Existing transaction found for transaction marked with propagation ‘never’
总结
以非事务方式执行,如果当前存在事务,则抛出异常
7、NESTED
7.1、父方法无事务,子方法开启事务,子方法报错
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Transactional(propagation = Propagation.NESTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:ks_a数据插入成功,ks_b数据回滚

7.2、父方法开启事务,子方法开启事务,子方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
}
@Override
@Transactional(propagation = Propagation.NESTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:数据都回滚

7.3、父方法开启事务,子方法开启事务,父方法报错
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insert(ksB);
throw new RuntimeException("主方法报错");
}
@Transactional(propagation = Propagation.NESTED)
public void insert(KsB ksB) {
ksBDao.insert(ksB);
}
结果:数据都回滚

7.4、父方法开启事务,子方法开启事务,子方法报错,父方法并捕获
@Transactional
public void add() {
KsA ksA = new KsA();
ksA.setName("林");
ksAService.insert(ksA);
try {
KsB ksB = new KsB();
ksB.setAge(10);
ksBService.insertError(ksB);
} catch (Exception e) {
//dosomething
}
}
@Transactional(propagation = Propagation.NESTED)
public void insertError(KsB ksB) {
ksBDao.insert(ksB);
throw new RuntimeException("子方法报错");
}
结果:ks_a数据插入成功,ks_b数据回滚

总结
如果当前没有事务,则新开事务执行
如果当前存在事务,则在嵌套事务内执行
四、结论
1、NESTED和REQUIRES区别
区别在于:如果当前存在事务,子方法抛异常时
NESTED在父方法可以选择捕获子方法,父方法数据不会回滚;
REQUIRES无论捕不捕获,父方法数据都回滚
2、NESTED和REQUIRES_NEW区别
区别:如果当前存在事务,父方法抛异常时
NESTED数据回滚,REQUIRES也是如此
REQUIRES_NEW数据不回滚
3、七种传播行为总结
说明:加入该事务,指的是父、子方法共用一个事务(无论父、子方法报错,整体回滚)
REQUIRED
父方法无事务,子方法开启新事务
父方法有事务,就加入该事务
SUPPORTS
如果当前不存在事务,就以非事务执行
如果当前存在事务,就加入该事务
MANDATORY
如果当前不存在事务,就抛出异常
如果当前存在事务,就加入该事务
REQUIRES_NEW
无论当前存不存在事务,都创建新事务
NOT_SUPPORTED
以非事务方式执行,如果当前存在事务,父方法以事务方式执行,子方法以非事务方式执行
NEVER
以非事务方式执行,如果当前存在事务,则抛出异常
NESTED
如果当前没有事务,则新开事务执行
如果当前存在事务,则在嵌套事务内执行
Spring事务传播行为实战的更多相关文章
- Spring事务传播特性的浅析——事务方法嵌套调用的迷茫
Spring事务传播机制回顾 Spring事务一个被讹传很广说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务.结果造成开发人员在设计事务方法时束手束脚,生怕一不小心就踩到地雷. 其实这 ...
- 阿里大牛带你深入分析spring事务传播行为
spring框架封装了很多有用的功能和组件,便于在项目开发中快速高效的调用,其中spring的事务使用非常简单,只需要在用到事务的地方加一行注解即可: 1@Transactional 但越是看起来简单 ...
- spring事务传播机制实例讲解
http://kingj.iteye.com/blog/1680350 spring事务传播机制实例讲解 博客分类: spring java历险 天温习spring的事务处理机制,总结 ...
- Spring事务传播机制
Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务 ...
- Spring事务传播机制和数据库隔离级别
Spring事务传播机制和数据库隔离级别 转载 2010年06月26日 10:52:00 标签: spring / 数据库 / exception / token / transactions / s ...
- spring 事务传播机制
spring 事务 传播机制 描述的 事务方法直接相互调用,父子事物开启,挂起,回滚 等的处理方式. 绿色的 那几个 我认为比较重要. 1 , @Transactional(propagation=P ...
- 事务、事务特性、事务隔离级别、spring事务传播特性
事务.事务特性.事务隔离级别.spring事务传播特性 1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功, ...
- 什么是事务、事务特性、事务隔离级别、spring事务传播特性
1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功,要么失败). 2.事务特性: 事务特性分为四个:原子性(At ...
- spring事务传播实现源码分析
转载. https://blog.csdn.net/qpfjalzm123/article/details/83717367 本文只是对spring事务传播实现的流程进行简单的分析,如有不对之处请指出 ...
随机推荐
- mybatis 08: 返回主键值的insert操作 + 利用UUID获取字符串(了解)
返回主键值的insert操作 应用背景 图示说明 在上述业务背景下,涉及两张数据表的关联操作:用户表 + 用户积分表 传统操作:在对用户表执行完插入语句后,再次查询该用户的uid,将该uid作为外键, ...
- Linux 12 安装Docker
参考源 https://www.bilibili.com/video/BV187411y7hF?spm_id_from=333.999.0.0 版本 本文章基于 CentOS 7.6 这里使用 yum ...
- FTP 基础 与 使用 Docker 搭建 Vsftpd 的 FTP 服务
FTP 基础 与 使用 Docker 搭建 Vsftpd 的 FTP 服务 前言 最近的工作中,需要将手机上的文件发送到公司的 FTP 的服务器.按照从前的思路,自然是,先将文件传到电脑,再由电脑上传 ...
- DL基础:cs231n assignment 2
cs231n assignment 2 20210913 - 20211005. 目录 cs231n assignment 2 fully-connected nets 基本思想 编程细节 复习mul ...
- 刷题记录:Codeforces Round #739 (Div. 3)
Codeforces Round #739 (Div. 3) 20210907.网址:https://codeforces.com/contest/1560. --(叹). A 不希望出现带" ...
- KubeEdge:下一代云原生边缘设备管理标准DMI的设计与实现
摘要:KubeEdge设备管理架构的设计实现,有效帮助用户处理设备数字孪生进程中遇到的场景. 本文分享自华为云社区<KubeEdge:下一代云原生边缘设备管理标准DMI的设计与实现>. 随 ...
- 【MySQL】从入门到精通5-一对多-外键
上期:[MySQL]从入门到掌握4-主键与Unique 第一章:创建角色表 啥是一对多啊? 一个账号可以有多个角色,但是一个角色只能属于一个账号. 举个例子,我们之前创建的是玩家的账号数据库. 但是一 ...
- QT的字符编码
QString编码:UTF-16 QString内部保存的数据就是QChar数组,是Unicode编码(utf16),在字符显示,操作的时候都是基于Unicode. QString构造时默认采用Lat ...
- 《Java Web程序设计——开发环境搭建》
Java Web程序设计--开发环境搭建 一.JDK下载.安装以及环境变量的配置: 官网:https://www.oracle.com/java/technologies/download ...
- linux安装node和达梦数据库8
PS.本次测试只是为了项目需要,但是在部署和启动程序的时候发生了一系列的报错,由此记录下来为日后作参考 安装达梦数据库 1. 达梦数据库(DM8)简介 达梦数据库管理系统是武汉达梦公司推出的具有完全自 ...