在 Spring 框架中,事务管理主要有两种方式:声明式事务 和 编程式事务。两者各有优势,适用于不同的业务场景。
一、声明式事务
1. 实现机制
声明式事务基于 AOP(面向切面编程),通过代理机制(如 JDK 动态代理或 CGLIB)在方法执行前后插入事务管理逻辑。开发者只需通过注解或 XML 配置声明事务规则,Spring 会自动处理事务的开启、提交和回滚。
2. 常见注解
@Transactional
:最常用的注解,用于类或方法上,声明事务边界。- 可配置属性:
propagation
:事务传播行为(如 REQUIRED、REQUIRES_NEW 等)isolation
:事务隔离级别timeout
:事务超时时间readOnly
:是否只读事务rollbackFor
/noRollbackFor
:指定哪些异常触发回滚
3. 优点
- 解耦业务逻辑与事务管理:业务代码中无需嵌入事务控制逻辑。
- 易于维护和扩展:通过配置即可修改事务行为。
- 支持事务传播行为:适合复杂业务逻辑中的事务嵌套、传播等场景。
- 符合开闭原则:新增业务逻辑不影响事务配置。
4. 缺点
- 灵活性较低:无法在方法内部动态控制事务边界。
- 自调用问题:若一个类内部调用另一个加了
@Transactional
的方法,事务可能不生效(因为代理失效)。 - 异常处理需谨慎:默认只对
RuntimeException
和Error
回滚,需手动配置rollbackFor
来处理 checked exception。
5. 使用场景
- 常规 CRUD 操作:如增删改查,事务边界清晰。
- 多层调用链:如 Service 层调用多个 DAO 方法,需要统一事务管理。
- 需要事务传播行为的场景:如一个事务方法调用另一个事务方法,需控制传播方式。
二、编程式事务
1. 实现机制
编程式事务通过手动编写事务控制代码来管理事务,通常使用 TransactionTemplate
或直接使用 PlatformTransactionManager
。开发者需要显式调用事务的开始、提交和回滚操作。
2. 常见方式
- 使用
TransactionTemplate
:
transactionTemplate.execute(status -> {
// 业务逻辑
return null;
});
- 使用
PlatformTransactionManager
:
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
// 业务逻辑
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
}
3. 优点
- 事务控制更灵活:可在代码中动态决定事务边界。
- 适合复杂事务逻辑:如根据条件决定是否提交或回滚。
- 避免自调用问题:因为事务控制是显式的,不受代理限制。
4. 缺点
- 代码侵入性强:业务代码中混杂事务控制逻辑。
- 可维护性差:事务逻辑与业务逻辑耦合,不易扩展。
- 容易出错:手动控制事务边界容易遗漏提交或回滚。
5. 使用场景
- 需要动态控制事务边界:如根据条件决定是否提交。
- 跨多个方法调用:如多个方法调用之间需要共享同一个事务上下文。
- 需要事务回调或事务监听:如事务提交后执行某些操作。
- 在非 Spring 管理的类中使用事务:如工具类或非 Spring Bean。
三、总结
特性 | 声明式事务 | 编程式事务 |
---|---|---|
实现方式 | AOP + 注解/配置 | 手动编写事务控制代码 |
灵活性 | 较低 | 高 |
可维护性 | 高 | 低 |
代码侵入性 | 无 | 有 |
异常处理 | 默认只回滚 RuntimeException | 可自定义回滚策略 |
事务边界控制 | 固定(方法级) | 动态(可精确控制) |
适用场景 | 常规业务逻辑 | 复杂事务逻辑、动态控制 |
四、示例代码
1. 声明式事务
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void registerUser(User user) throws Exception {
userRepository.save(user);
// 模拟异常
if (user.getName().equals("error")) {
throw new Exception("注册失败");
}
}
}
2. 编程式事务(使用 TransactionTemplate)
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private UserRepository userRepository;
public void registerUser(User user) {
transactionTemplate.execute(status -> {
try {
userRepository.save(user);
if (user.getName().equals("error")) {
status.setRollbackOnly();
throw new RuntimeException("注册失败");
}
return null;
} catch (Exception e) {
status.setRollbackOnly();
throw e;
}
});
}
}