记一次 @Transactional 事务未回滚的调查
🏷️ Spring Boot
前几天维护公司的一个项目,发现 Controller
的方法上虽然加了 @Transactional(rollbackFor = Exception.class)
注解,这个方法中也没有加 try catch
处理,但是发生异常时事务并没有回滚。
常见的事务未起作用多是因为方法是内部调用的、方法被定义为 private
或者异常被捕捉了但没有 throw
出去。
排查时在方法中添加了 try catch
处理,并在 catch
中 throw
了捕捉的异常。仔细查看日志发现,事务确实开启了,异常也确实被捕捉并 throw
了出去。但是,事务也确实没有 rollback ,而是在最后正常 commit 了。
java
Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@334e353]
Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@334e353]
Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@334e353]
通过 debug 发现,异常发生后,代码运行到了使用 AOP 创建的 around 处理中,而在这个处理中,就有 try catch
处理。这是才想起来, AOP 本质上就是通过创建代理类实现的,而 @Transactional
也是基于 AOP 实现的。
这是一个用于记录请求日志的 AOP ,其代码如下:
java
@Pointcut("execution(public * com.example.app.controller.*.*(..))")
public void webLog() {
}
@Around("webLog()")
public Object arround(ProceedingJoinPoint pjp) {
try {
// 业务处理
Object obj = pjp.proceed();
// ... 一些日志处理
return obj;
} catch (Throwable t) {
logger.error(t.getMessage(), t);
return ResponseInfo.buildError("请求错误");
}
}
在这里 @Transactional
注解相当于添加到了 around 方法上,异常被捕获然后返回了一个业务的错误响应。没有 throw
异常,所以也就没有触发 rollback ,最终事务正常提交了。
知道原因就好处理了,记录异常日志后,将异常再抛出去就可以了,而原本的返回的业务错误,可以通过 @ControllerAdvice
+ @ExceptionHandler
的全局异常处理来实现。
java
@Around("webLog()")
public Object arround(ProceedingJoinPoint pjp) throws Throwable {
try {
//业务处理
Object obj = pjp.proceed();
// ... 一些日志处理
return obj;
} catch (Throwable t) {
logger.error(t.getMessage(), t);
throw t;
}
}