Spring Boot 事务管理
在 Spring Boot 框架里,@Transactional
注解是实现声明式事务管理的关键工具。下面从多个维度详细剖析这个注解,帮助你深入理解并熟练运用它。
1. 注解作用
借助 AOP(面向切面编程)技术,@Transactional
能够在方法执行前开启事务,方法成功执行后提交事务,若方法执行过程中出现异常则回滚事务。这样一来,业务代码就无需直接和事务 API 打交道,开发人员可以把更多精力放在业务逻辑上。
2. 核心属性
2.1 propagation
(传播行为)
此属性用于确定事务方法和现有事务之间的关系,共有 7 种传播行为,常见的有以下几种:
- REQUIRED(默认值):若当前不存在事务,就创建一个新事务;若存在事务,就加入该事务。
- REQUIRES_NEW:不管当前是否存在事务,都会创建一个新事务,并且挂起当前事务。
- SUPPORTS:如果当前存在事务,就加入该事务;如果不存在事务,就以非事务的方式执行。
- NOT_SUPPORTED:以非事务的方式执行操作,若当前存在事务,就挂起该事务。
- MANDATORY:必须在一个已存在的事务中执行,若当前不存在事务,就抛出异常。
2.2 isolation
(隔离级别)
该属性用于控制事务之间的可见性,常见的隔离级别有:
- DEFAULT(默认值):采用数据库的默认隔离级别(例如 MySQL 的
REPEATABLE_READ
,Oracle 的READ_COMMITTED
)。 - READ_UNCOMMITTED:允许读取尚未提交的数据变更,可能会导致脏读、不可重复读和幻读问题。
- READ_COMMITTED:只允许读取已经提交的数据,可以避免脏读,但仍可能出现不可重复读和幻读。
- REPEATABLE_READ:确保在同一个事务中多次读取同一数据的结果是一致的,可以避免脏读和不可重复读,但幻读仍有可能发生。
- SERIALIZABLE:最高的隔离级别,通过强制事务串行执行来避免所有并发问题,但会降低数据库的性能。
2.3 rollbackFor
和 noRollbackFor
- rollbackFor:可以指定一个或多个异常类,当方法抛出这些异常时,事务会回滚。
- noRollbackFor:可以指定一个或多个异常类,当方法抛出这些异常时,事务不会回滚。
2.4 readOnly
将事务标记为只读,有助于数据库优化器对查询进行优化,一般用于只读取数据的方法。
2.5 timeout
用于设置事务的超时时间(单位为秒),若事务执行时间超过该时间仍未完成,就会自动回滚。
3. 使用示例
3.1 基础用法
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// 默认传播行为是 REQUIRED,默认回滚规则是 RuntimeException
@Transactional
public void createUser(User user) {
userRepository.save(user);
if (user.getAge() < 0) {
throw new IllegalArgumentException("年龄不能为负数");
}
}
}
3.2 自定义传播行为和隔离级别
@Service
public class OrderService {
@Autowired
private ProductService productService;
// 自定义传播行为和隔离级别
@Transactional(
propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = {SQLException.class, BusinessException.class}
)
public void processOrder(Order order) throws SQLException {
productService.reduceStock(order.getProductId(), order.getQuantity());
// 其他业务逻辑
}
}
3.3 只读事务
@Service
public class UserQueryService {
@Autowired
private UserRepository userRepository;
@Transactional(readOnly = true)
public List<User> getAllUsers() {
return userRepository.findAll();
}
}
4. 底层实现原理
- AOP 代理:Spring 会为带有
@Transactional
注解的类创建代理对象。 - TransactionInterceptor:这是一个拦截器,负责处理事务的开启、提交和回滚操作。
- PlatformTransactionManager:这是事务管理器接口,不同的持久化框架需要使用不同的实现类(如
DataSourceTransactionManager
用于 JDBC,JpaTransactionManager
用于 JPA)。
5. 注意事项
5.1 自调用问题
在同一个类中,一个方法调用另一个带有 @Transactional
注解的方法,事务不会生效。这是因为 AOP 代理需要通过外部代理对象来调用才能生效。
@Service
public class SelfCallService {
public void methodA() {
// 自调用,事务不会生效
methodB();
}
@Transactional
public void methodB() {
// 事务性操作
}
}
解决办法:
- 注入自身代理对象
@Service
public class SelfCallService {
@Autowired
private SelfCallService self;
public void methodA() {
// 通过代理对象调用,事务生效
self.methodB();
}
@Transactional
public void methodB() {
// 事务性操作
}
}
5.2 异常处理
- 未检查异常(RuntimeException 及其子类)和 Error 会触发默认的事务回滚。
- 检查异常(如 IOException)不会触发默认的事务回滚,需要通过
rollbackFor
来指定。
5.3 事务边界
事务的开始和结束是由方法的调用和返回决定的,因此要确保事务方法的原子性,避免事务范围过大。
6. 常见错误场景
6.1 事务不生效
- 方法不是 public 的。
- 自调用问题。
- 异常被捕获但没有重新抛出。
6.2 脏读/不可重复读/幻读
隔离级别设置不合理,例如在需要避免脏读的场景下使用了 READ_UNCOMMITTED
。
6.3 死锁
事务持有锁的时间过长,或者多个事务以不同的顺序获取锁,都可能导致死锁。
7. 最佳实践
- 优先在服务层方法上使用
@Transactional
注解。 - 保持事务方法的短小精悍,避免在事务中执行耗时操作(如网络调用)。
- 合理设置隔离级别和传播行为。
- 明确指定
rollbackFor
异常。 - 对于只读操作,使用
readOnly = true
来提高性能。
通过深入理解 @Transactional
注解的各个属性和使用场景,你可以在 Spring Boot 应用中更高效地管理事务,确保数据的一致性和完整性。
本文来自博客园,作者:杯酒-故人,转载请注明原文链接:https://www.cnblogs.com/BeiJiuGuRen/p/18903765