面试官让列举 Spring 的事务会失效的场景,我说了 8 个

Spring 事务不生效总览


简单来说,Spring 事务会在几种特定的场景下失效,如下图所示。



1、数据库不支持事务


Spring 事务生效的前提是所连接的数据库要支持事务,如果底层的数据库都不支持事务,则 Spring 的事务肯定会失效。例如,如果使用的数据库为 MySQL,并且选用了 MyISAM 存储引擎,则 Spring 的事务就会失效。


2、事务方法未被 Spring 管理


如果事务方法所在的类没有加载到 Spring IOC 容器中,也就是说,事务方法所在的类没有被 Spring 管理,则 Spring 事务会失效,示例如下。


public class ProductService {    @Autowired    private ProductDao productDao;
@Transactional(propagation = Propagation.REQUIRES_NEW) public void updateProductStockCountById(Integer stockCount, Long id) { productDao.updateProductStockCountById(stockCount, id); }}


ProductService 类上没有标注 @Service 注解,Product 的实例没有加载到 Spring IOC 容器中,就会造成 updateProductStockCountById() 方法的事务在 Spring 中失效。

3、方法没有被 public 修饰


如果事务所在的方法没有被 public 修饰,此时 Spring 的事务会失效,例如,如下代码所示。


@Servicepublic class ProductService {    @Autowired    private ProductDao productDao;
@Transactional(propagation = Propagation.REQUIRES_NEW) private void updateProductStockCountById(Integer stockCount, Long id) { productDao.updateProductStockCountById(stockCount, id); }}


虽然 ProductService 上标注了 @Service 注解,同时 updateProductStockCountById() 方法上标注了 @Transactional (propagation = Propagation。REQUIRES_NEW) 注解。
但是,由于 updateProductStockCountById() 方法为内部的私有方法(使用 private 修饰),那么此时 updateProductStockCountById() 方法的事务在 Spring 中会失效。
4、同一类中方法调用
如果同一个类中的两个方法分别为 A 和 B,方法 A 上没有添加事务注解,方法 B 上添加了 @Transactional 事务注解,方法 A 调用方法 B,则方法 B 的事务会失效。例如,如下代码所示。
@Servicepublic class OrderService {
@Autowired private OrderDao orderDao;
@Autowired private ProductDao productDao;
public void submitOrder() { //生成订单 Order order = new Order(); long number = Math.abs(new Random().nextInt(500)); order.setId(number); order.setOrderNo("order_" + number); orderDao.saveOrder(order); //减库存 this.updateProductStockCountById(1, 1 L); }
@Transactional(propagation = Propagation.REQUIRES_NEW) public void updateProductStockCountById(Integer stockCount, Long id) { productDao.updateProductStockCountById(stockCount, id); }}

submitOrder() 方法和 updateProductStockCountById() 方法都在 OrderService 类中,submitOrder() 方法上没有标注事务注解,updateProductStockCountById() 方法上标注了事务注解,submitOrder() 方法调用了 updateProductStockCountById() 方法,此时,updateProductStockCountById() 方法的事务在 Spring 中会失效。
5、未配置事务管理器
如果在项目中没有配置 Spring 的事务管理器,即使使用了 Spring 的事务管理功能,Spring 的事务也不会生效。
例如,没有在项目的配置类中配置如下代码。
@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {    return new DataSourceTransactionManager(dataSource);}

此时,Spring 的事务就会失效。
6、方法的事务传播类型不支持事务
如果内部方法的事务传播类型为不支持事务的传播类型,则内部方法的事务在 Spring 中会失效。
例如,如下代码所示。
@Servicepublic class OrderService {    @Autowired    private OrderDao orderDao;    @Autowired    private ProductDao productDao;
@Transactional(propagation = Propagation.REQUIRED) public void submitOrder() { //生成订单 Order order = new Order(); long number = Math.abs(new Random().nextInt(500)); order.setId(number); order.setOrderNo("order_" + number); orderDao.saveOrder(order); //减库存 this.updateProductStockCountById(1, 1 L); }
@Transactional(propagation = Propagation.NOT_SUPPORTED) public void updateProductStockCountById(Integer stockCount, Long id) { productDao.updateProductStockCountById(stockCount, id); }}

由于 updateProductStockCountById() 方法的事务传播类型为 NOT_SUPPORTED,不支持事务,则 updateProductStockCountById() 方法的事务会在 Spring 中失效。
7、不正确的捕获异常
不正确的捕获异常也会导致 Spring 的事务失效,示例如下。
@Servicepublic class OrderService {    @Autowired    private OrderDao orderDao;    @Autowired    private ProductDao productDao;

@Transactional(propagation = Propagation.REQUIRED) public void submitOrder() { //生成订单 Order order = new Order(); long number = Math.abs(new Random().nextInt(500)); order.setId(number); order.setOrderNo("order_" + number); orderDao.saveOrder(order); //减库存 this.updateProductStockCountById(1, 1 L); }
@Transactional(propagation = Propagation.REQUIRED) public void updateProductStockCountById(Integer stockCount, Long id) { try { productDao.updateProductStockCountById(stockCount, id); int i = 1 / 0; } catch (Exception e) { logger.error("扣减库存异常:", e.getMesaage()); } }}

   

updateProductStockCountById() 方法中使用 try-catch 代码块捕获了异常,即使 updateProductStockCountById() 方法内部会抛出异常,但也会被 catch 代码块捕获到,此时 updateProductStockCountById() 方法的事务会提交而不会回滚,并且 submitOrder() 方法的事务会提交而不会回滚,这就造成了 Spring 事务的回滚失效问题。


8、错误的标注异常类型


如果在 @Transactional 注解中标注了错误的异常类型,则 Spring 事务的回滚会失效,示例如下。


@Transactional(propagation = Propagation.REQUIRED)public void updateProductStockCountById(Integer stockCount, Long id) {    try {        productDao.updateProductStockCountById(stockCount, id);    } catch (Exception e) {        logger.error("扣减库存异常:", e.getMesaage());        throw new Exception("扣减库存异常");    }}


在 updateProductStockCountById() 方法中捕获了异常,并且在异常中抛出了 Exception 类型的异常,此时,updateProductStockCountById() 方法事务的回滚会失效。


为何会失效呢?这是因为 Spring 中对于默认回滚的事务异常类型为 RuntimeException,上述代码抛出的是 Exception 异常。


默认情况下,Spring 事务中无法捕获到 Exception 异常,所以此时 updateProductStockCountById() 方法事务的回滚会失效。


此时可以手动指定 updateProductStockCountById() 方法标注的事务异常类型,如下所示。


@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)


这里,需要注意的是:Spring 事务注解 @Transactional 中的 rollbackFor 属性可以指定 Throwable 异常类及其子类。


- EOF -

推荐阅读  点击标题可跳转

1、美团二面:spring事务不生效的15种场景

2、Spring 在多线程环境下如何确保事务一致性

3、61 张图,剖析 Spring 事务,就是要钻到底!


看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

点赞和在看就是最大的支持❤️




相关推荐

  • AI新突破:提示词生成工具打造Agent,模拟人生
  • 背调电话打给了我撕破脸的前同事,新工作飞了
  • 使用Python分析瑞幸和星巴克全国门店分布关系
  • 一个不太好用的 IDEA 代码生成插件
  • 区区几行Python代码,自动探索性数据分析!
  • 实践教程|scikit-learn 的建模万能模板
  • 看完稚晖君的最新工作台,我酸了。。
  • 技术人对抗焦虑的加减法
  • 大厂面试必考:多线程
  • 聊一聊 React 和 Vue 有啥不一样
  • 【HBLOG10月内推】让你的职业生涯腾飞!
  • 从原理到实践,分析 Redisson 分布式锁的实现方案
  • Java 中的 Stream 可以替代 for 循环吗?
  • 干掉 powerdesigner,设计数据库表用它就够了!
  • 大多数公司,都对 AGPL 许可证“敬而远之”
  • 保护生产中 Node.js 应用程序安全的 15 项最佳实践
  • 从0到1实现一个前端监控系统(附源码)
  • 语雀发布故障公告,个人用户可领6个月会员服务
  • LeCun又双叒唱衰自回归LLM:GPT-4的推理能力非常有限,有两篇论文为证
  • 前端线上部署,如何通知用户有新版本???