写在前面

之前遇到一些自以为是的面试官问了一大推关于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事务管理)相关推荐

  1. Spring事务失效 -方法内部调用

    首先感谢网友的文章 Spring事务失效的2种情况 JDK动态代理给Spring事务埋下的坑 前提知识: 两个前提 1 注解使用 spring容器的事务管理注解 @org.springframewor ...

  2. Spring@Cacheable注解在类内部调用失效的问题

    如图所示,getRecomendGoogs方法里面调用findImgUrlByName方法,在findImgUrlByName方法上使用@Cacheable注解的时候,并没有走缓存. 要解决这个问题, ...

  3. Collection 属性ArrayList.add方法内部调用过程

    Collection 属性 //二进制搜索阈值 private static final int BINARYSEARCH_THRESHOLD = 5000; //改变阈值 private stati ...

  4. 阿里Arthas(阿尔赛斯)-trace查看方法内部调用路径及耗时

    1.trace用法 参数名称                        参数说明 class-pattern                类名表达式匹配 method-pattern       ...

  5. 多方法调用 一个出错 集体回滚_一个@Transaction哪里来这么多坑?

    前言 在之前的文章中已经对Spring中的事务做了详细的分析了,这篇文章我们来聊一聊平常工作时使用事务可能出现的一些问题(本文主要针对使用@Transactional进行事务管理的方式进行讨论)以及对 ...

  6. 问题(待完成):微服务,失败回滚?保持事务的原子性?多步骤调用,如何来实现...

    问题(待完成):微服务,失败回滚?保持事务的原子性?多步骤调用,如何来实现 转载于:https://www.cnblogs.com/panpanwelcome/p/9104746.html

  7. Java实现 for循环输出空心的菱形 在main方法中调用printHollowRhombus()方法完成10行的空心菱形输出,其中 printHollo

    Java实现for循环输出空心的菱形 @author asus 在main方法中调用printHollowRhombus()方法完成10行的空心菱形输出,其中 printHollowRhombus() ...

  8. SpringBoot异常处理回滚事务详解(自动回滚、手动回滚、部分回滚)(事务失效)...

    参考:https://blog.csdn.net/zzhongcy/article/details/102893309 概念 事务定义 事务,就是一组操作数据库的动作集合.事务是现代数据库理论中的核心 ...

  9. mysql 事务 回滚 原理_mysql 事务的实现原理

    一. 开篇 相信大家都用过事务以及了解他的特点,如原子性(Atomicity),一致性(Consistency),隔离型(Isolation)以及持久性(Durability)等.今天想跟大家一起研究 ...

  10. mysql 事务回滚语句_数据库事务回滚语句-sql事务回滚语句是-用于事务回滚的sql语句...

    sql 回滚语句 这种情况的数据恢复只能利用事务日志的备份来进行,所以如果你的SQL没有进行相应的全库备份 或不能备份日志(truncate log on checkpoint选项为1),那幺就无法进 ...

最新文章

  1. Cstring的使用
  2. 【博客搬家旧文】剑指offer [ java ] 面试题10 斐波那契数列
  3. Linux CentOS7 中 完美解决VMTools失效,windows 与 Liunx间完美复制文件,无报错的解决方案
  4. 通过子类化创建新的层和模型
  5. Java线程之CompletionService
  6. 使用jstack和TDA进行java线程dump分析
  7. 如何使用USGS下载DEM数据
  8. 爬虫python是什么意思_python爬虫是什么? 【黑马程序员】
  9. 网络广告投放基础,广告
  10. Shapely 扩展包功能札记
  11. 电脑怎么修改html5,详细教你怎么设置电脑默认浏览器
  12. 10个免费的PHP编辑器/开发工具推荐
  13. 吊炸天,Spring Security还有这种用法
  14. poi版本冲突导致连续报错NoSuchMethodError、VerticalAlignment无法转换为short和ClassNotFoundException的解决办法及兼容性问题解决
  15. 计算机网络——访问控制列表
  16. 五月飞花轻折柳 OpenStack黑客松在苏州等你
  17. 《STM32单片机开发应用教程(HAL库版)—基于国信长天嵌入式竞赛实训平台(CT117E-M4)》第四章4.9 TIM---输入捕获(脉冲频率测量)实验
  18. html中去除浮漂有什么作用,浮漂有动作,但总是锚鱼该怎么办?只需4招,保证帮你解决问题...
  19. 解决string not in pwd
  20. 【渝粤教育】国家开放大学2018年秋季 2370T汽车故障诊断技术(A) 参考试题

热门文章

  1. 2021年应届生,找java后端开发要什么水平才算合格?
  2. 手把手教你如何将图片“嵌入”网页中
  3. 将Android Studio的设置恢复到初始化(清除所有的设置)
  4. POJ - 1179
  5. 宋体小四在手机上是几号_word文档4号宋体 word宋体小四是几号字
  6. 微信小程序 谈谈在大学初次写项目的体验
  7. python控制风扇_Python 语音控制普通风扇实现教程
  8. linux 禁用超线程,Linux动态启用/禁用超线程技术
  9. c语言写死循环程序,通过简单的例子看c程序死循环
  10. [1125]AES加密报错:Illegal key size or default parameters