[SpringBoot] 事务处理机制修饰的方法,如何正确设计异常处理?

6 阅读2分钟

在Spring Boot项目中,使用@Transactional(rollbackFor = Exception.class)修饰的方法内设计异常处理时,需注意以下关键点:

1. 异常抛出机制

  • 不要吞没异常:若方法内部通过try-catch捕获了异常但未重新抛出,事务将不会回滚。即使配置了rollbackFor = Exception.class,Spring仍需感知到异常才能触发回滚。

  • 正确示例

    @Transactional(rollbackFor = Exception.class)
    public void updateData() throws CustomException {
        try {
            // 业务逻辑
        } catch (Exception e) {
            throw new CustomException("操作失败", e); // 抛出受检异常
        }
    }
    

2. 嵌套事务处理

  • 传播行为选择:若存在嵌套事务(如方法A调用方法B),需根据业务场景选择传播级别:

    • Propagation.REQUIRES_NEW:内部事务独立提交/回滚,不影响外部事务。
    • Propagation.NESTED:内部事务通过保存点(Savepoint)实现局部回滚,外部事务提交时整体生效。
  • 示例配置

    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public void innerMethod() { ... }
    

3. 自调用问题

  • 避免同类调用:若事务方法被同一类中的其他方法调用(非代理调用),事务注解会失效。解决方案:

    • 将事务方法拆分到另一个Bean中。
    • 使用AopContext.currentProxy()获取代理对象调用。

4. 全局异常处理

  • 统一捕获异常:通过@ControllerAdvice@ExceptionHandler全局处理异常,避免事务方法内耦合异常响应逻辑。

  • 示例

    @ControllerAdvice
    public class GlobalExceptionHandler {
        @ExceptionHandler(Exception.class)
        public ResponseEntity<ErrorResponse> handleException(Exception e) {
            // 记录日志并返回统一错误格式
            return ResponseEntity.status(500).body(new ErrorResponse("系统异常"));
        }
    }
    

5. 手动回滚标记

  • 强制回滚场景:若需在捕获异常后不抛出但仍需回滚,可手动标记事务状态:

    @Transactional(rollbackFor = Exception.class)
    public void updateData() {
        try {
            // 业务逻辑
        } catch (Exception e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }
    }
    

总结

  • 核心原则:确保异常能被Spring事务管理器感知(抛出或手动标记回滚)。
  • 传播级别:根据业务需求选择REQUIRES_NEWNESTED隔离嵌套事务。
  • 代码结构:通过全局异常处理解耦事务与响应逻辑,避免自调用陷阱。