1. Spring 中的 @Transactional 注解深度解析
从事Java开发这么多年,我见过太多因为事务使用不当导致的生产事故。记得有一次凌晨两点被叫起来处理问题,就是因为某个转账操作没有正确配置事务,导致金额扣了但对方账户没收到钱。今天我就结合15年踩坑经验,把@Transactional这个看似简单实则暗藏玄机的注解给大家讲透。
1.1 事务的本质与价值
事务(Transaction)本质上是一组不可分割的数据库操作序列。它的核心特性可以用ACID来概括:
- 原子性(Atomicity):事务内的操作要么全部成功,要么全部失败
- 一致性(Consistency):事务执行前后数据库状态保持一致
- 隔离性(Isolation):并发事务之间互不干扰
- 持久性(Durability):事务提交后修改永久生效
在实际业务中,最典型的例子就是银行转账:
java复制public void transfer(Long fromId, Long toId, BigDecimal amount) {
accountMapper.debit(fromId, amount); // 扣款
accountMapper.credit(toId, amount); // 存款
}
如果没有事务保护,当扣款成功但存款失败时,系统就会出现数据不一致。而加上@Transactional后,Spring会帮我们自动处理这种异常情况。
1.2 声明式 vs 编程式事务
Spring提供了两种事务管理方式:
| 方式 | 实现 | 优点 | 缺点 |
|---|---|---|---|
| 编程式事务 | 手动编写TransactionTemplate | 控制粒度细 | 代码侵入性强 |
| 声明式事务 | 使用@Transactional注解 |
代码简洁,非侵入式 | 控制粒度较粗 |
现在99%的场景都会选择声明式事务,只有在需要精细控制事务边界时才会考虑编程式。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. @Transactional 工作原理剖析
2.1 AOP代理机制
Spring事务的实现核心是AOP(面向切面编程)。当我们给方法加上@Transactional后,Spring会通过动态代理生成一个代理对象。这个代理对象的工作流程如下:
- 方法调用前开启事务(设置autoCommit=false)
- 执行目标方法
- 方法正常结束时提交事务
- 方法抛出异常时回滚事务
可以通过以下代码验证代理机制:
java复制@Service
public class UserService {
@Transactional
public void createUser(User user) {
// 实际会打印出代理类名
System.out.println(this.getClass().getName());
}
}
输出结果会是类似UserService$$EnhancerBySpringCGLIB$$12345678这样的代理类名。
2.2 事务传播行为详解
传播行为(Propagation)定义了多个事务方法相互调用时的事务边界。这是最容易出问题的地方,我们来看具体场景:
java复制@Service
public cl
