事务处理注解使用方法详解

事务处理注解的基本作用

在开发Web应用时,经常会遇到需要保证数据一致性的场景。比如用户下单,要同时扣库存、生成订单、扣款,这三个操作必须全部成功,否则就得回滚。这时候就需要用到事务。Spring框架提供了@Transaction注解,让事务管理变得简单直观。

如何启用事务注解

要在Spring项目中使用事务注解,首先得确保配置了@EnableTransactionManagement。如果你用的是Spring Boot,通常只要引入了spring-boot-starter-data-jpa或spring-boot-starter-jdbc,这个功能就自动启用了。

基本用法示例

在Service层的方法上加上@org.springframework.transaction.annotation.Transactional,就能开启事务控制。

public class OrderService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private StockRepository stockRepository;

    @Transactional
    public void createOrder(Order order) {
        stockRepository.decreaseStock(order.getProductId(), order.getCount());
        orderRepository.save(order);
        // 如果这里抛出异常,上面两步都会回滚
    }
}

注解的常用属性

事务注解不是只能写个@Transactional就完事,它还支持很多配置项,用来应对不同场景。

比如只读事务,适合查询类操作,数据库可以做优化:

@Transactional(readOnly = true)
public List<Order> getUserOrders(Long userId) {
    return orderRepository.findByUserId(userId);
}

再比如指定异常类型才回滚,默认情况下只有运行时异常才会触发回滚,检查型异常不会。如果希望遇到IOException也回滚,可以这样写:

@Transactional(rollbackFor = IOException.class)
public void importData() throws IOException {
    // 数据导入逻辑
}

事务传播行为设置

有时候一个事务方法调用了另一个事务方法,这时候就得考虑传播行为。最常见的就是REQUIRED,表示如果有事务就加入,没有就新建。

@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
    innerService.innerMethod(); // 内部方法也会在同一事务中
}

如果内部方法想独立事务运行,可以用REQUIRES_NEW:

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {
    // 即使外部事务回滚,这个方法一旦提交就不会受影响
}

实际开发中的注意事项

事务注解是加在public方法上的,如果用在private方法,Spring的AOP机制捕获不到,事务就失效了。

另外,同一个类内方法调用也会绕过代理,导致事务不生效。比如下面这种情况:

public void methodA() {
    methodB(); // 这样调用,methodB的事务不起作用
}

@Transactional
public void methodB() {
    // 事务逻辑
}

解决办法是通过ApplicationContext获取当前Bean的代理对象,或者重构代码结构。

还有一个常见问题是异常被捕获后没抛出,导致事务无法触发回滚:

@Transactional
public void riskyOperation() {
    try {
        doSomething();
    } catch (Exception e) {
        log.error("失败了", e);
        // 错误做法:吞掉异常,事务不会回滚
    }
}

正确的做法是捕获后重新抛出,或者抛出自定义异常并配合rollbackFor使用。