Spring事务管理A方法内部调用B方法的回滚问题(springboot事务管理)
写在前面
之前遇到一些自以为是的面试官问了一大推关于A方法内部调用B方法的事务问题,换着法的问,AB方法分别设置不同的事务传播属性时,如果AB方法中某个方法出错了,AB方法数据会怎样回滚的问题?
首先这种问题很不负责任,前提条件不是很清晰,问题没说清楚,容易成了绕口令游戏:
1、A和B方法有没有对异常进行try catch ?
2、AB方法可设置的事务传播属性组合那么多,怎么能口头上给你一一列举?
3、报错的位置是在数据库操作方法之前还是之后?
4、AB方法是否是来自同一个类的两个方法,还是来自两个不同类的方法?
5、事务的超时时间怎么设置的?
这样的问题,前提条件没说清楚,如果把注意力放在什么AB方法,异常位置的判断上,就跳进了坑里去
其实决定事务是否回滚的本质很简单,不需要考虑什么AB方法:
事务超时时间内,方法所属事务内是否抛出可以触发回滚的异常,有则回滚。没有就不回滚
如果你认同我的观点,后面的内容就没必要继续看了
1、spring中事务控制API介绍
PlatformTransactionManager接口
它是一个接口,是spring的事务管理器核心接口,spring并不实现具体的事务,而是负责包装底层事务,提供规范。应用底层支持什么样的事务策略,spring就支持什么样的事务策略。该接口提供了我们操作事务的常用方法。如下:
TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException;void commit(TransactionStatus var1) throws TransactionException;void rollback(TransactionStatus var1) throws TransactionException;
我们在开发中都是直接使用它的实现类,有如下常用实现类:
TransactionDefinition接口()
它是一个接口,是定义事务的信息对象,提供了如下常用的方法:
1、事务隔离级别
2、事务隔离级别
3、事务超时时间
public interface TransactionDefinition {int PROPAGATION_REQUIRED = 0; //事务传播属性int PROPAGATION_SUPPORTS = 1;int PROPAGATION_MANDATORY = 2;int PROPAGATION_REQUIRES_NEW = 3;int PROPAGATION_NOT_SUPPORTED = 4;int PROPAGATION_NEVER = 5;int PROPAGATION_NESTED = 6;int ISOLATION_DEFAULT = -1; //事务隔离级别int ISOLATION_READ_UNCOMMITTED = 1;int ISOLATION_READ_COMMITTED = 2;int ISOLATION_REPEATABLE_READ = 4;int ISOLATION_SERIALIZABLE = 8;int TIMEOUT_DEFAULT = -1; //事务超时时间default int getPropagationBehavior() {return 0;}default int getIsolationLevel() {return -1;}default int getTimeout() {return -1;}default boolean isReadOnly() {return false;}@Nullabledefault String getName() {return null;}static TransactionDefinition withDefaults() {return StaticTransactionDefinition.INSTANCE;}
}
事务传播属性
propagation | 说明 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,则新建一个事务执行 |
PROPAGATION_SUPPORTS | 支持当前事务,如果没有当前事务,则以非事务的方式执行 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,则抛出异常 |
PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前已经有事务了,则将当前事务挂起 |
PROPAGATION_NOT_SUPPORTED | 不支持当前事务,而且总是以非事务方式执行 |
PROPAGATION_NEVER | 不支持当前事务,如果存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前事务存在,则在嵌套事务中执行,否则行为类似于PROPAGATION_REQUIRED。 EJB中没有类似的功能。 |
默认是PROPAGATION_REQUIRED的事务传播。
事务隔离级别
说明:事务隔离级别,反应了事务在并发访问时的处理态度。
ISOLATION_DEFAULT:
默认级别,归属于下列某一种隔离级别。在项目中使用默认值即可。
ISOLATION_READ_UNCOMMITTED:
可以读取其他事务未提交的数据(脏读)
ISOLATION_READ_COMMITTED:
只读取其他事务已经提交的数据,解决脏读的问题。有可能两次读取不一致的问题,不可重复读(oracle数据库默认级别)
ISOLATION_REPEATABLE_READ:
是否读取其他事务提交修改后的数据,解决不可重复读的问题。保证两次读取一致,可重复读(mysql数据库默认级别)
**ISOLATION_SERIALIZABLE:**是否读取其他事务添加后的数据,解决幻影读的问题
细节:
1.事务级别从低到高:脏读->不可重复读->可重复读->解决幻读
2.事务级别越高,数据越安全,消耗的资源越多,数据库操作性能越低
3.在企业项目中,使用哪一种级别的事务,需要根据业务需求来确定
事务超时时间
以秒为单位进行设置。如果设置为-1(默认值),表示没有超时限制。在企业项目中使用默认值即可。
2、决定事务回滚的本质
1、不是一个方法就是一个事务,事务和方法不是对等关系。一个事务可以管理多个方法的数据库操作
2、如何判断多个方法的数据库操作被同一个事务管理,需要看事务的传播属性
3、事务超时时间内,方法所属事务内是否抛出可以触发回滚的异常,有则回滚。没有就不回滚
测试事务回滚的本质
注意:本文测试代码
实体类–成本表和收入表
初始值如图所示:
/*** 成本实体类*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
@Table(name = "tb_cost")
@ToString(callSuper = true)
public class Cost {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;@Column(name = "cost")private BigDecimal cost;}/*** 收入实体类*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
@Entity
@Table(name = "tb_revenue")
@ToString(callSuper = true)
public class Revenue {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;@Column(name = "revenue")private BigDecimal revenue;}
Dao数据库访问类
@Repository
public interface RevenueDao extends JpaRepository<Revenue, Integer> {}@Repository
public interface CostDao extends JpaRepository<Cost, Integer> {}
service类
RevenueService的addRevenue内部调用costService.addCost方法
@Service
public class RevenueService {@Resourceprivate RevenueDao revenueDao;@Resourceprivate PlatformTransactionManager transactionManager;@Resourceprivate CostService costService;/**** @param id 查询的id* @param addNum 增加的数字* @param needRevenueServiceThrowExp 是否需要在RevenueService抛出异常* @param needCostServiceThrowExp 是否需要在CostService抛出异常* @param REVENUE_PROPAGATION RevenueService的事务传播属性* @param COST_PROPAGATION CostService的事务传播属性*/public void addRevenue(Integer id, BigDecimal addNum, Boolean needRevenueServiceThrowExp, Boolean needCostServiceThrowExp, int REVENUE_PROPAGATION, int COST_PROPAGATION) {TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);transactionTemplate.setPropagationBehavior(REVENUE_PROPAGATION);transactionTemplate.execute(status -> {Revenue revenue = revenueDao.findById(id).get();revenue.setRevenue(revenue.getRevenue().add(addNum));revenueDao.save(revenue);costService.addCost(id, addNum,needCostServiceThrowExp, COST_PROPAGATION);if (needRevenueServiceThrowExp) {throw new RuntimeException("RevenueService手动报错");}return true;});}}
CostService类
@Service
public class CostService {@Resourceprivate CostDao costDao;@Resourceprivate PlatformTransactionManager transactionManager;@Asyncpublic void addCost(Integer id, BigDecimal addNum, Boolean needThrowExp, int COST_PROPAGATION) {TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);transactionTemplate.setPropagationBehavior(COST_PROPAGATION);transactionTemplate.execute(status -> {Cost cost = costDao.findById(id).get();cost.setCost(cost.getCost().add(addNum));costDao.save(cost);if (needThrowExp) {throw new RuntimeException("CostService手动报错");}return true;});}}
论证1:不是一个方法就是一个事务,事务和方法不是对等关系。一个事务可以管理多个方法的数据库操作
测试方法:
@Testvoid contextLoads() {BigDecimal addNum = BigDecimal.ONE;revenueService.addRevenue(1, addNum, true,false, TransactionDefinition.PROPAGATION_REQUIRED,TransactionDefinition.PROPAGATION_REQUIRED);}
costService.addCost和revenueService.addRevenue被同一个事务管理。即时costService.addCost运行正常,但revenueService.addRevenue报错时,一样会回滚
论证2、如何判断多个方法的数据库操作被同一个事务管理,需要看事务的传播属性
测试方法
@Testvoid contextLoads() {BigDecimal addNum = BigDecimal.ONE;revenueService.addRevenue(1, addNum, true,false, TransactionDefinition.PROPAGATION_REQUIRED,TransactionDefinition.PROPAGATION_REQUIRES_NEW);}
costService.addCost和revenueService.addRevenuede的事务传播属性不一样。costService.addCost是PROPAGATION_REQUIRES_NEW,新起一个事务,不会报错就提交事务成功。revenueService.addRevenue报错时,会回滚。因为他们被不同的事务管理
论证3、方法所属事务内是否出现触发回滚的异常,有则回滚。没有就不回滚
测试方法
@Testvoid contextLoads() {BigDecimal addNum = BigDecimal.ONE;revenueService.addRevenue(1, addNum, true,true, TransactionDefinition.PROPAGATION_REQUIRED,TransactionDefinition.PROPAGATION_NOT_SUPPORTED);}
同时结合论证2的例子:
costService.addCost是PROPAGATION_REQUIRES_NEW,新起一个事务,不会报错就提交事务成功。
@Testvoid contextLoads() {BigDecimal addNum = BigDecimal.ONE;revenueService.addRevenue(1, addNum, true,false, TransactionDefinition.PROPAGATION_REQUIRED,TransactionDefinition.PROPAGATION_REQUIRES_NEW);}
Spring事务管理A方法内部调用B方法的回滚问题(springboot事务管理)相关推荐
- Spring事务失效 -方法内部调用
首先感谢网友的文章 Spring事务失效的2种情况 JDK动态代理给Spring事务埋下的坑 前提知识: 两个前提 1 注解使用 spring容器的事务管理注解 @org.springframewor ...
- Spring@Cacheable注解在类内部调用失效的问题
如图所示,getRecomendGoogs方法里面调用findImgUrlByName方法,在findImgUrlByName方法上使用@Cacheable注解的时候,并没有走缓存. 要解决这个问题, ...
- Collection 属性ArrayList.add方法内部调用过程
Collection 属性 //二进制搜索阈值 private static final int BINARYSEARCH_THRESHOLD = 5000; //改变阈值 private stati ...
- 阿里Arthas(阿尔赛斯)-trace查看方法内部调用路径及耗时
1.trace用法 参数名称 参数说明 class-pattern 类名表达式匹配 method-pattern ...
- 多方法调用 一个出错 集体回滚_一个@Transaction哪里来这么多坑?
前言 在之前的文章中已经对Spring中的事务做了详细的分析了,这篇文章我们来聊一聊平常工作时使用事务可能出现的一些问题(本文主要针对使用@Transactional进行事务管理的方式进行讨论)以及对 ...
- 问题(待完成):微服务,失败回滚?保持事务的原子性?多步骤调用,如何来实现...
问题(待完成):微服务,失败回滚?保持事务的原子性?多步骤调用,如何来实现 转载于:https://www.cnblogs.com/panpanwelcome/p/9104746.html
- Java实现 for循环输出空心的菱形 在main方法中调用printHollowRhombus()方法完成10行的空心菱形输出,其中 printHollo
Java实现for循环输出空心的菱形 @author asus 在main方法中调用printHollowRhombus()方法完成10行的空心菱形输出,其中 printHollowRhombus() ...
- SpringBoot异常处理回滚事务详解(自动回滚、手动回滚、部分回滚)(事务失效)...
参考:https://blog.csdn.net/zzhongcy/article/details/102893309 概念 事务定义 事务,就是一组操作数据库的动作集合.事务是现代数据库理论中的核心 ...
- mysql 事务 回滚 原理_mysql 事务的实现原理
一. 开篇 相信大家都用过事务以及了解他的特点,如原子性(Atomicity),一致性(Consistency),隔离型(Isolation)以及持久性(Durability)等.今天想跟大家一起研究 ...
- mysql 事务回滚语句_数据库事务回滚语句-sql事务回滚语句是-用于事务回滚的sql语句...
sql 回滚语句 这种情况的数据恢复只能利用事务日志的备份来进行,所以如果你的SQL没有进行相应的全库备份 或不能备份日志(truncate log on checkpoint选项为1),那幺就无法进 ...
最新文章
- Cstring的使用
- 【博客搬家旧文】剑指offer [ java ] 面试题10 斐波那契数列
- Linux CentOS7 中 完美解决VMTools失效,windows 与 Liunx间完美复制文件,无报错的解决方案
- 通过子类化创建新的层和模型
- Java线程之CompletionService
- 使用jstack和TDA进行java线程dump分析
- 如何使用USGS下载DEM数据
- 爬虫python是什么意思_python爬虫是什么? 【黑马程序员】
- 网络广告投放基础,广告
- Shapely 扩展包功能札记
- 电脑怎么修改html5,详细教你怎么设置电脑默认浏览器
- 10个免费的PHP编辑器/开发工具推荐
- 吊炸天,Spring Security还有这种用法
- poi版本冲突导致连续报错NoSuchMethodError、VerticalAlignment无法转换为short和ClassNotFoundException的解决办法及兼容性问题解决
- 计算机网络——访问控制列表
- 五月飞花轻折柳 OpenStack黑客松在苏州等你
- 《STM32单片机开发应用教程(HAL库版)—基于国信长天嵌入式竞赛实训平台(CT117E-M4)》第四章4.9 TIM---输入捕获(脉冲频率测量)实验
- html中去除浮漂有什么作用,浮漂有动作,但总是锚鱼该怎么办?只需4招,保证帮你解决问题...
- 解决string not in pwd
- 【渝粤教育】国家开放大学2018年秋季 2370T汽车故障诊断技术(A) 参考试题
热门文章
- 2021年应届生,找java后端开发要什么水平才算合格?
- 手把手教你如何将图片“嵌入”网页中
- 将Android Studio的设置恢复到初始化(清除所有的设置)
- POJ - 1179
- 宋体小四在手机上是几号_word文档4号宋体 word宋体小四是几号字
- 微信小程序 谈谈在大学初次写项目的体验
- python控制风扇_Python 语音控制普通风扇实现教程
- linux 禁用超线程,Linux动态启用/禁用超线程技术
- c语言写死循环程序,通过简单的例子看c程序死循环
- [1125]AES加密报错:Illegal key size or default parameters