当前位置: 首页 > article >正文

SpringBooot之事务失效的场景

在Spring Boot中,你可以通过使用@Transactional注解来给方法增加事务支持,但是有时候使用不当会导致事务失败,会产生数据无法一致性这种严重的数据问题,可能出现的场景如下。

一、实例背景介绍

1、数据库数据表sys_user中description字段“要求非空”,但是我们新增时候传null,继而导致保存失败,事务被触发导致批量数据会全部会被回滚。

2、Springboot中使用事务需要在启动类中开启事务@EnableTransactionManagement和在方法或者类上使用注解@Transactional

二、数据库引擎不支持事务

这里以 MySQL 为例,其 MyISAM 引擎是不支持事务操作的,InnoDB 才是支持事务的引擎,一般要支持事务都会使用 InnoDB。

从 MySQL 5.5.5 开始的默认存储引擎是:InnoDB,之前默认的都是:MyISAM,所以这点要值得注意,底层引擎不支持事务再怎么操作都是无济于事。

三、没有被 Spring 管理

// @Service
public class OrderServiceImpl implements OrderService {
 
    @Transactional
    public void updateOrder(Order order) {
        // update order
    }
}

上方例子中,@Service注解被注释了。这个类就不会被加载成一个 Bean,那这个类就不会被 Spring 管理了,事务自然就失效了。

四、事务方法非public修饰

由于Spring的事务是基于AOP的方式结合动态代理来实现的。因此事务方法一定要是public的,这样才能便于被Spring做事务的代理和增强。

而且,在Spring内部也会有一个 org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource类,去检查事务方法的修饰符。

    @ApiOperation("添加")
    @PostMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    protected void addUser(@RequestBody User user) {
        user.setUserName("火箭");
        user.setDescription("Rocket");
        iUserService.save(user);

        user.setUserName("巫师");
        user.setDescription(null);
        iUserService.save(user);
    }

五、非事务方法调用事务方法 

Spring会给带有@Transactional注解类生成一个动态代理对象,对方法做增加实现事务效果。

非事务方法直接调用,就会导致对象无法被Spring代理对象获取到,继而导致事务失败。

@ApiOperation("添加")
    @PostMapping("/add")
    //@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void addUser(@RequestBody User user) {
        this.saveUser(user);
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void saveUser(User user) {
        user.setUserName("火箭");
        user.setDescription("Rocket");
        iUserService.save(user);

        user.setUserName("巫师");
        user.setDescription(null);
        iUserService.save(user);
    }

六、事务方法的异常被捕获

而Spring的事务管理就是要感知业务方法的异常,当捕获到异常后才会回滚事务,

现在异常被捕获,且没有抛出,相当于异常被程序员私下处理掉了,就会导致Spring无法感知事务异常,自然不会回滚,事务就失效了。

@ApiOperation("添加")
    @PostMapping("/add")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void addUser(@RequestBody User user) {
        try{
            user.setUserName("火箭");
            user.setDescription("Rocket");
            iUserService.save(user);

            user.setUserName("巫师");
            user.setDescription(null);
            iUserService.save(user);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

七、事务异常类型不对

Spring的事务管理默认感知的异常类型是RuntimeException,当事务方法内部抛出了一个IOException时,不会被Spring捕获,因此就不会触发事务回滚,事务就失效了。 

1、异常被处理,没有正确抛出
@Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
 
        }
    }
}

try..catch..捕获了一场,然而catch中还没有将异常抛出,所以springboot捕获不到异常,就无法进行回滚! 

2、异常抛出类型不对
@Service
public class OrderServiceImpl implements OrderService {

    @Transactional
    public void updateOrder(Order order) {
        try {
            // update order
        } catch {
            throw new Exception("更新错误");
        }
    }
}

try..catch..虽然捕获了异常且跑出了Exception异常,但是springboot只有捕获到RuntimeException异常时才会触发回滚。 

八、事务传播行为设置不对

1、因为事务之间的传播会相互影响,一定要慎用传播行为,注意外部事务与内部事务之间的关系。

@Transactional
public void createOrder(){
    // 生成订单
    insertOrder();
    // 扣减库存
    reduceStock();
    throw new RuntimeException("业务异常");
}

@Transactional  // 默认的是如果当前没有事务,自己创建事务,如果有事务则加入
public void insertOrder() {

}

// 不管当前方法所在方法有没有都开启一个事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void reduceStock() {}

在示例代码中,事务的入口是createOrder()方法,会开启一个事务,可以成为外部事务。在createOrder()方法内部又调用了insertOrder()方法和reduceStock()方法。这两个都是事务方法。

不过,reduceStock()方法的事务传播行为是REQUIRES_NEW,这会导致在进入reduceStock()方法时会创建一个新的事务,可以成为子事务。insertOrder()则是默认,因此会与createOrder()合并事务。

因此,当createOrder方法最后抛出异常时,只会导致insertOrder方法回滚,而不会导致reduceStock方法回滚,因为reduceStock是一个独立事务。 

2、 Propagation.NOT_SUPPORTED: 表示不以事务运行,当前若存在事务则挂起。这是主动配置了事务不生效,所以事务就不存在。

 @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void updateOrder(Order order) {
        // update order
    }

http://www.kler.cn/a/382713.html

相关文章:

  • C语言 循环高级
  • Java 异常处理的最佳实践
  • Uniapp安装Pinia并持久化(Vue3)
  • 翻译工具开发技术笔记:《老挝语翻译通》app支持语音识别翻译功能,怎么提高语音识别的准确度呢?
  • 推荐程序员好用的浏览器插件
  • 2024最新的开源博客系统:vue3.x+SpringBoot 3.x 前后端分离
  • 护肤品类电商代运营的公司介绍与分析
  • 【Docker】X-DOC:使用WSL在Windows中体验Linux发行版安装桌面版Docker
  • 在 MacOS 上跑 kaldi
  • Java+控制台 商城销售系统
  • 【动态规划 数学】2745. 构造最长的新字符串|1607
  • Web Workers 学习笔记
  • 【QT】Qt文件和多线程
  • SSLHandshakeException错误解决方案
  • Flutter常用命令整理
  • Halcon 矫正图像 图像矫正
  • CustomDataSource、Entity 和 Primitive 区别
  • MongoDB笔记02-MongoDB基本常用命令
  • 小程序 + AI 自动直播:一部手机开启抖音挂载小程序流量主变现之旅
  • 搭建react项目
  • Markdown转HTML
  • 前深度学习时代-经典的推荐算法
  • 《JVM第7课》堆区
  • qt QTextStream详解
  • ssm基于Web的汽车客运订票系统的设计与实现+vue
  • 解决return code from pthread_create() is 22报错问题