<!-- # 记一次 @Transactional 事务未回滚的调查 --> <!-- transactional-can-not-work-research --> 前几天维护公司的一个项目,发现 `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; } } ``` Loading... 版权声明:本文为博主「佳佳」的原创文章,遵循 CC 4.0 BY-NC-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://www.liujiajia.me/2022/8/29/transactional-can-not-work-research 提交