Spring 事务失效场景:异常处理、异常类型与方法权限问题

202 阅读2分钟

引言

在 Spring 应用开发里,事务管理是保障数据一致性的关键功能。但在实际编码过程中,因各种不当操作,常常会出现事务失效的情况。我们将围绕异常捕获处理、抛出检查异常以及非 public 方法这三个常见场景,深入探讨事务失效的原因和对应的解决办法。

一、异常捕获处理后未抛出导致事务失效

1. 问题描述

在使用 Spring 事务时,如果在事务方法里手动捕获了异常,却没有重新抛出,Spring 就无法感知到异常,进而不会触发事务回滚。

com.example.service.OrderService.java
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void createOrder() {
        try {
            orderRepository.save(new Order());
            // 模拟异常
            int result = 1 / 0; 
        } catch (Exception e) {
            // 捕获异常但未抛出
            e.printStackTrace(); 
        }
    }
}

在上述代码中,createOrder 方法捕获了异常并进行了简单处理,没有重新抛出,即便发生异常,事务也不会回滚。

2. 解决方案

手动将捕获的异常重新抛出,让 Spring 能够感知到异常从而触发事务回滚。

com.example.service.OrderService.java
@Service
public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Transactional
    public void createOrder() throws Exception {
        try {
            orderRepository.save(new Order());
            // 模拟异常
            int result = 1 / 0; 
        } catch (Exception e) {
            // 手动抛出异常
            throw e; 
        }
    }
}

二、抛出检查异常导致事务失效

1. 问题描述

Spring 默认只对 RuntimeException 及其子类和 Error 进行事务回滚。若抛出的是检查异常(即 Exception 的子类,但不是 RuntimeException 及其子类),事务不会回滚。

com.example.service.UserService.java
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional
    public void saveUser() throws Exception {
        userRepository.save(new User());
        // 抛出检查异常
        throw new Exception("Test Exception"); 
    }
}

2. 解决方案

在 @Transactional 注解中通过 rollbackFor 属性明确指定需要回滚的异常类型,将其设置为 Exception.class,这样所有异常都能触发事务回滚。

com.example.service.UserService.java
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    @Transactional(rollbackFor = Exception.class)
    public void saveUser() throws Exception {
        userRepository.save(new User());
        // 抛出检查异常
        throw new Exception("Test Exception"); 
    }
}

三、非 public 方法导致事务失效

1. 问题描述

Spring 的事务管理是基于 AOP 代理实现的,而 AOP 代理只会对 public 方法进行增强。若将 @Transactional 注解加在非 public 方法上,事务会失效。

com.example.service.ProductService.java
@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Transactional
    private void updateProduct() {
        productRepository.updateStock();
        // 模拟异常
        throw new RuntimeException("Test Exception"); 
    }
}

2. 解决方案

把方法的访问权限修改为 public,让 Spring AOP 代理能够对其进行事务增强。

com.example.service.ProductService.java
@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Transactional
    public void updateProduct() {
        productRepository.updateStock();
        // 模拟异常
        throw new RuntimeException("Test Exception"); 
    }
}

总结

Spring 事务失效在异常捕获处理、抛出检查异常以及非 public 方法这三个场景下较为常见。开发时,捕获异常后要记得手动抛出,对检查异常需配置 rollbackFor 属性,同时要保证事务方法为 public 访问权限。