事务处理注解的基本作用
在开发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使用。