• 解决Spring子事务新开事务REQUIRES_NEW仍被主事务回滚问题


    解决子事务新开事务被主事务回滚问题

    Spring提供的事务传播机制:

    1.REQUIRED (默认):支持当前事务,如果当前没有事务,则新建事务,如果当前存在事务,则加入当前事务,合并成一个事务,如果一个方法发生异常回滚,则整个事务回滚。

    2.REQUIRES_NEW:新建事务,如果当前存在事务,则把当前事务挂起,这个方法会独立提交事务,不受调用者的事务影响,父级异常,它也是正常提交,但如果是此方法发生异常未被捕获处理,且异常满足父级事务方法回滚规则,则父级方法事务会被回滚。

    问题:

    现有如下处理逻辑:

    消息队列中读取消息开始配置设备信息-> 设备信息入表,预占资源 -> 发生异常时在catch代码块中的处理逻辑如下:

    catch (Exception e) {
                log.info(orderId + "自动配端口失败:{}", e);
                try {
                    autoToErr(orderId + "配置出现异常,转手工配置");
                } catch (Exception exception) {
                    throw exception;
                }
             }
         }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    核心操作在于:

    autoToErr(orderId + "配置出现异常,转手工配置");
    
    • 1

    表示自动配置失败,将配置失败的订单信息存入err表,记录下来用于转手工配置

    但是产生了如下问题:

    • 当未开启事务时,如果发生异常,记录err表成功,但是设备信息已入表,资源已被预占无法释放!

    针对上述问题,在方法上开启注解,并在发生异常时回滚

    @Transactional(rollbackFor = Exception.class)
    public JSONObject autoAssignSplitter(String orderId) throws Exception {
        ...
            try {
                ...
            } catch(...) {
                autoToErr(orderId + "配置出现异常,转手工配置");
            }
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    但是会出现,处理失败后,预占资源被释放,但是插入err表的操作也被回滚掉

    所以根据上述事务传播机制,在aotoToManualAssign方法上声明其事务传播机制为REQUIRES_NEW

    // 新建事务,不受调用该事务的父事务回滚影响
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void autoToErr(String failMsg) 
        throws Exception {
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 但是又发生了新问题:声明的事务传播机制没有生效,还是全部回滚

    原因分析:
    在这里插入图片描述

    打印发现:父事务和子事务的调用对象不是同一个代理对象,这就是为什么添加REQUIRES_NEW不起作用,想要让注解生效就要用代理对象的方法。

    所以我们需要把代理对象暴露出来,并使用其来调用子事务,来保证结果正确性

    最终实现的代码:

    1. 使用@EnableAspectJAutoProxy,并将exposeProxy设置为true,默认为false

      public @interface EnableAspectJAutoProxy {
          boolean proxyTargetClass() default false;
      
          boolean exposeProxy() default false;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    @EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true)
    public class ConfigController {
    	...
    }
    
    • 1
    • 2
    • 3
    • 4
    1. 在子事务调用时使用代理对象:
    ((ConfigPortBiz)AopContext.currentProxy()).autoToErr(orderId + "配置出现异常,转手工配置");
    
    • 1
    1. 将子事务传播机制声明为REQUIRES_NEW
    // 新建事务,不受调用该事务的父事务回滚影响
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void autoToErr(String failMsg) 
        throws Exception {
        ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
  • 相关阅读:
    【NLP】情绪分析与酒店评论
    Google的工程实践指南(下):代码开发者指南
    mybatis拦截器实现数据权限
    Azkaban Flow 1.0 的使用
    哈希的应用--位图和布隆过滤器
    vue 滚动缩放反转图片功能
    基于百度文心大模型全面重构,小度正式推出AI原生操作系统DuerOS X
    prometheus helm install 如何配置告警模版
    “可编程网络”的基础概念介绍
    go admin基础开发
  • 原文地址:https://blog.csdn.net/Lionel_SSL/article/details/126835821