• SpringBoot 关于异步与事务一起使用的问题


    最近遇到的一个场景,在一个被 @Transactional 注解的方法A中中调用了一个被 @Async 注解标记的方法B,由于方法B 在执行时方法A 的事务没有提交,但是方法B在执行过程中获取不到方法A中尚未提交的数据,从而最终倒是方法B执行异常。

    @Transactional
    public void create(User user){
      // 如果用户已存在,则先删除
      delete(user.id);
      // 创建用户
      int userId = insert(user);
      //  更新用户信息
      update(userId);
    }
    
    @Async
    public void update(Integer userId){
      Icon icon = getUserIcon(userId);
      // 更新用户图片
      updateUserPohot(userId,icon);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    像上面的代码,我为创建用户的方法上标记了@Transactional事务注解,然后在其中调用了update()更新方法,这个方法上标记了@Async 注解。这样代码虽然看起来没有什么问题,但是实际在执行update()方法时,由于是其他线程去执行的,就会导致有可能 create()方法对应的事务还没有提交,update() 方法就无法读取到新插入的 user 记录,从而导致更新失败。

    解决方案

    通过调整逻辑保证事务在调用异步方法前被提交

    这个问题的原因是由于 @Transactional 和 @Async 注解一起使用导致的,那么我们可以从这个方向入手,首先我们可以先确认将create()方法的事务提交后,然后再去执行异步更新方法:

    public void create(User user){
      int userId = doCreate(user);
      //  更新用户信息
      update(userId);
    }
    
    @Transactional
    public void doCreate(User user){
        // 如果用户已存在,则先删除
      delete(user.id);
      // 创建用户
      return insert(user);
    }
    
    @Async
    public void update(Integer userId){
      Icon icon = getUserIcon(userId);
      // 更新用户图片
      updateUserPohot(userId,icon);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    异步方法放在事务方法外调用,这样异步方法就能够读取到已经提交的事务数据了。

    或者我们还可以使用TransactionTemplate来代替 @Transactional 注解:

    @Autowired
    TransactionTemplate transactionTemplate;
    
    public void create(User user){
      int userId = transactionTemplate.execute(status->{
        // 如果用户已存在,则先删除
        delete(user.id);
        // 创建用户
        return insert(user);
      });
      //  更新用户信息
      update(userId);
    }
    
    @Async
    public void update(Integer userId){
      Icon icon = getUserIcon(userId);
      // 更新用户图片
      updateUserPohot(userId,icon);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    通过 TransactionTemplate细化了事务粒度,可以保证在调用异步方法前事务已经被提交。

    上面的方案基本都能 解决问题,下面是从网上找到的,spring 给出的解决方案:

    @Transactional
    public void create(User user){
      // 如果用户已存在,则先删除
      delete(user.id);
      // 创建用户
      int userId = insert(user);
      TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
        @Override
        public void afterCommit() {
          //  更新用户信息
          update(userId);
        }
      });
    }
    
    @Async
    public void update(Integer userId){
      Icon icon = getUserIcon(userId);
      // 更新用户图片
      updateUserPohot(userId,icon);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    通过将异步方法注册为事务提交后的操作,这样Spring可以自动帮我们在事务提交后执行对应的操作。


    参考资料

    异步事务?关于异步@Async + 事务
    @Transactional 事务提交 与 @Async 异步执行

  • 相关阅读:
    IDEA快捷键大全
    CD206抗体载Fe3O4的PLGA纳米微球/多烯紫杉醇-PLGA纳米粒的制备研究
    uniapp 使用web-view外接三方
    Linux线程1
    npm run build 打包报错 - 添加 parallel: false, 解决
    分销系统开发|如何正确定义三级分销?
    C# 常用功能整合-1
    『忘了再学』Shell流程控制 — 34、if条件判断语句(二)
    pxe+kickstart无人值守装机
    计算机毕业设计ssm+vue基本微信小程序的客户资源管理系统
  • 原文地址:https://blog.csdn.net/qq_19922839/article/details/126322800