解决子事务新开事务被主事务回滚问题
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不起作用,想要让注解生效就要用代理对象的方法。
所以我们需要把代理对象暴露出来,并使用其来调用子事务,来保证结果正确性
最终实现的代码:
-
使用@EnableAspectJAutoProxy,并将exposeProxy设置为true,默认为false public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
@EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true)
public class ConfigController {
...
}
- 在子事务调用时使用代理对象:
((ConfigPortBiz)AopContext.currentProxy()).autoToErr(orderId + "配置出现异常,转手工配置");
- 将子事务传播机制声明为REQUIRES_NEW
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void autoToErr(String failMsg)
throws Exception {
...
}
|