解决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;
            }
         }
     }

核心操作在于:

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

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

但是产生了如下问题:

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

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

@Transactional(rollbackFor = Exception.class)
public JSONObject autoAssignSplitter(String orderId) throws Exception {
    ...
        try {
            ...
        } catch(...) {
            autoToErr(orderId + "配置出现异常,转手工配置");
        }
    ...
}

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

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

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

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

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

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

最终实现的代码:

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

    public @interface EnableAspectJAutoProxy {
        boolean proxyTargetClass() default false;
    
        boolean exposeProxy() default false;
    }
    
@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true)
public class ConfigController {
	...
}
  1. 在子事务调用时使用代理对象:
((ConfigPortBiz)AopContext.currentProxy()).autoToErr(orderId + "配置出现异常,转手工配置");
  1. 将子事务传播机制声明为REQUIRES_NEW
// 新建事务,不受调用该事务的父事务回滚影响
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void autoToErr(String failMsg) 
    throws Exception {
    ...
}

你可能感兴趣的:(技术,Spring,spring,java,后端)