前言
在 Java 开发中,事务管理 是数据库操作的重要保障,而 Spring 提供的 @Transactional
注解 让事务管理变得简单而强大。然而,很多开发者对这个注解的理解还停留在“加了就能回滚”的层面,而忽略了它背后的机制和高级用法。
今天,我们就来深入解析 @Transactional
注解,从基础到源码,帮你彻底掌握事务管理的精髓!
🎯 一、@Transactional 注解简介
@Transactional
是 Spring 提供的声明式事务管理注解,用于简化事务边界的定义,让开发者通过注解的方式控制事务的行为。
基本用法:
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void addUser(String username) {
userRepository.save(new User(username));
// 模拟异常,测试事务回滚
int result = 1 / 0;
}
}
解释:
@Transactional
会让 Spring 在执行addUser()
时开启一个事务。- 当代码抛出 未捕获的运行时异常(如
ArithmeticException
)时,事务会自动回滚。
效果:
- 如果没有异常:数据正常入库。
- 如果发生异常:所有数据库操作回滚,不插入数据。
⚡ 二、@Transactional 的核心属性
@Transactional
提供了一些灵活的配置项,让你可以更细粒度地控制事务的行为:
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = Exception.class,
noRollbackFor = ArithmeticException.class,
readOnly = false
)
常见属性解释:
-
propagation(传播行为)
控制当前方法的事务边界与外部事务的关系:传播行为 含义 REQUIRED 默认值,有事务就加入,没有就新建 REQUIRES_NEW 无论如何都新建事务 SUPPORTS 有事务则加入,没有则以非事务方式运行 NOT_SUPPORTED 以非事务方式运行,不加入事务 MANDATORY 必须在现有事务中运行,否则抛异常 NEVER 不能有事务,否则抛异常 NESTED 嵌套事务,内层事务回滚不影响外层事务 -
isolation(隔离级别)
设置数据库事务的隔离级别:隔离级别 含义 DEFAULT 使用数据库默认的隔离级别 READ_UNCOMMITTED 允许读取未提交数据(可能导致脏读) READ_COMMITTED 只能读取已提交数据(避免脏读) REPEATABLE_READ 可重复读(避免脏读、不可重复读) SERIALIZABLE 串行化(最高级,避免一切并发问题 -
timeout(超时时间)
设置事务超时时间,单位秒。超过时间事务会自动回滚:
@Transactional(timeout = 5) // 5秒内未完成则回滚
4.readOnly(只读事务) 提示数据库优化查询,减少锁竞争:
@Transactional(readOnly = true) // 仅执行查询操作
5.rollbackFor / noRollbackFor(回滚规则)
- rollbackFor:指定哪些异常触发回滚
- noRollbackFor:指定哪些异常不触发回滚
@Transactional(rollbackFor = {IOException.class, SQLException.class})
📚 三、@Transactional 的应用场景
✅ 1️⃣ 基础事务管理
@Transactional
public void createOrder(String userId, String productId) {
deductStock(productId); // 减库存
createOrderRecord(userId, productId); // 新增订单
}
说明:
如果任意一步失败,事务回滚,数据保持一致性。
✅ 2️⃣ 嵌套事务(Propagation.NESTED)
当方法调用链中可能存在嵌套逻辑时,可以使用嵌套事务:
@Transactional
public void outerMethod() {
userService.addUser(); // 主事务
try {
userService.updateUser(); // 内部事务
} catch (Exception e) {
// 捕获异常但不影响外部事务
}
}
如果 addUser()
成功,但 updateUser()
失败,
外层事务不会回滚,只有 updateUser()
的子事务会回滚。
✅ 3️⃣ 只读事务(readOnly = true)
适合查询类方法:
@Transactional(readOnly = true)
public User findUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
效果:
- 避免加锁
- 提升查询性能
- 提示数据库优化查询路径
🔥 四、@Transactional 的注意事项
1️⃣ 事务只有在代理对象调用时才生效
如果你在同一个类中调用带有 @Transactional
的方法,事务不会生效:
public class UserService {
@Transactional
public void methodA() {
methodB(); // 内部调用,不会触发事务
}
@Transactional
public void methodB() {
// 数据库操作
}
}
✅ 正确做法: 通过代理调用:
@Autowired
private UserService userService;
public void callMethod() {
userService.methodB(); // 触发事务
}
2️⃣ 检查异常不会触发回滚
Spring 默认只有 未检查异常(继承自 RuntimeException
)才触发回滚:
// 不会触发回滚
@Transactional
public void method() throws IOException {
throw new IOException("Checked Exception!");
}
✅ 解决方法:
@Transactional(rollbackFor = Exception.class)
3️⃣ 事务失效的场景
- 方法是
private
或final
- 未通过代理对象调用
- 数据库不支持事务(如 MyISAM 引擎)
- ....
更多事务实效场景深度解析及解决方案:传送门
💡 五、@Transactional 源码解析
当你使用 @Transactional
时,Spring 会创建一个代理对象,并在方法执行前后添加事务逻辑:
核心流程:
1️⃣ 代理对象调用方法时,触发 TransactionInterceptor
。
2️⃣ TransactionInterceptor
调用 TransactionManager
,开启事务。
3️⃣ 方法执行完毕后,提交或回滚事务。
TransactionStatus status = transactionManager.getTransaction(definition);
try {
result = joinPoint.proceed();
transactionManager.commit(status);
} catch (Throwable ex) {
transactionManager.rollback(status);
throw ex;
}
🎁 六、总结
✨ 你学到了:
@Transactional
的基本用法- 常用属性:传播行为、隔离级别、超时时间等
- 实际应用场景:嵌套事务、只读事务等
- 注意事项:事务失效的常见原因
- 底层原理:代理对象 + 事务拦截器
💡 思考题:
- 为什么同一个类中调用
@Transactional
方法事务会失效? - 如何实现一个简单的事务管理器?
如果你觉得这篇文章对你有帮助,请点赞⭐、收藏📌、关注🚀!