一、什么是事务传播行为?

事务传播行为(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事务传播行为实战的更多相关文章

  1. Spring事务传播特性的浅析——事务方法嵌套调用的迷茫

    Spring事务传播机制回顾 Spring事务一个被讹传很广说法是:一个事务方法不应该调用另一个事务方法,否则将产生两个事务.结果造成开发人员在设计事务方法时束手束脚,生怕一不小心就踩到地雷. 其实这 ...

  2. 阿里大牛带你深入分析spring事务传播行为

    spring框架封装了很多有用的功能和组件,便于在项目开发中快速高效的调用,其中spring的事务使用非常简单,只需要在用到事务的地方加一行注解即可: 1@Transactional 但越是看起来简单 ...

  3. spring事务传播机制实例讲解

    http://kingj.iteye.com/blog/1680350   spring事务传播机制实例讲解 博客分类:   spring java历险     天温习spring的事务处理机制,总结 ...

  4. Spring事务传播机制

    Spring在TransactionDefinition接口中规定了7种类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务 ...

  5. Spring事务传播机制和数据库隔离级别

    Spring事务传播机制和数据库隔离级别 转载 2010年06月26日 10:52:00 标签: spring / 数据库 / exception / token / transactions / s ...

  6. spring 事务传播机制

    spring 事务 传播机制 描述的 事务方法直接相互调用,父子事物开启,挂起,回滚 等的处理方式. 绿色的 那几个 我认为比较重要. 1 , @Transactional(propagation=P ...

  7. 事务、事务特性、事务隔离级别、spring事务传播特性

    事务.事务特性.事务隔离级别.spring事务传播特性   1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功, ...

  8. 什么是事务、事务特性、事务隔离级别、spring事务传播特性

    1.什么是事务: 事务是程序中一系列严密的操作,所有操作执行必须成功完成,否则在每个操作所做的更改将会被撤销,这也是事务的原子性(要么成功,要么失败). 2.事务特性: 事务特性分为四个:原子性(At ...

  9. spring事务传播实现源码分析

    转载. https://blog.csdn.net/qpfjalzm123/article/details/83717367 本文只是对spring事务传播实现的流程进行简单的分析,如有不对之处请指出 ...

随机推荐

  1. Python逆向爬虫之pyquery,非常详细

    系列目录 Python逆向爬虫之pyquery pyquery是一个类似jquery的python库,它实现能够在xml文档中进行jQuery查询,pyquery使用lxml解析器进行快速在xml和h ...

  2. python自动化测试-列表、元组、字典学习笔记

    1.列表 格式:  L = [1,2,3,5]    M = [7,8,9]  print(type(L)) -> :list 列表增加元素: print(L.append(10))   -&g ...

  3. 【NOI P模拟赛】混凝土粉末(整体二分)

    题面 样例输入 5 8 1 1 4 2 2 3 1 2 3 3 1 2 5 1 2 3 3 2 5 2 2 1 2 2 1 3 样例输出 1 0 4 0 1 0 样例解释 题解 比这道题简单了不知多少 ...

  4. HDU2065 “红色病毒”问题 (指数型母函数经典板题)

    题面 医学界发现的新病毒因其蔓延速度和Internet上传播的"红色病毒"不相上下,被称为"红色病毒",经研究发现,该病毒及其变种的DNA的一条单链中,胞嘧啶, ...

  5. 【java】学习路线5-public和private、构造方法、this关键字、封装对象、static关键字、main方法结构解析

    //一个教务管理系统//知识点清单/*public & private 的区别一个是公开的,一个是私有的,作用域不一样,访问的权限不一样咯如果是用private修饰,则调用者只可以是在当前的作 ...

  6. 宝塔面板服务器ip地址修改域名

    参考博客:请点击百度 今天登录宝塔面板是突然忘记了服务器IP地址,从而导致了以下这种情况. 其实我以前是买过一个域名的,但是并没有绑定到宝塔上面.从而就一直拿IP登录宝塔面版.现在用命令方式更换域名, ...

  7. OpenJudge1.5.17

    20:球弹跳高度的计算 总时间限制: 1000ms 内存限制: 65536kB 描述 一球从某一高度落下(整数,单位米),每次落地后反跳回原来高度的一半,再落下. 编程计算气球在第10次落地时,共经过 ...

  8. 第六十三篇:Vue的条件渲染与列表渲染

    好家伙, 1.条件渲染v-if 1.1.v-if基本使用 <body> <div id="app"> <p v-if="flag" ...

  9. 这12款idea插件,能让你代码飞起来

    前言 基本上每个程序员都会写代码,但写代码的速度不尽相同. 为什么有些人,一天只能写几百行代码? 而有些人,一天可以写几千行代码? 有没有办法,可以提升开发效率,在相同的时间内,写出更多的代码呢? 今 ...

  10. 简析XDP的重定向机制

    GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. GreatSQL是MySQL的国产分支版本,使用上与MySQL一致. 一. XDP Socket示例解析 源码参见:htt ...