前言
@transactional 是 spring 中用于声明式事务管理的核心注解,它可以简化事务控制逻辑,通过注解方式替代传统的编程式事务管理。在 spring boot 中,@transactional 的使用尤为广泛,以下是其详细解析:
一、基本作用
@transactional 用于标记某个方法或类需要被事务管理,spring 会在方法执行前后自动开启、提交或回滚事务,确保操作的原子性(要么全成功,要么全失败)。
使用位置:
- 方法上:仅对该方法生效
- 类上:对类中所有公共方法生效(public)
- 接口上:不推荐(可能因代理模式导致失效)
二、核心属性
@transactional 有多个属性可配置,常用的包括:
属性名 | 作用 | 默认值 |
---|---|---|
rollbackfor | 指定哪些异常触发回滚(类或数组) | 仅 runtimeexception 及其子类 |
norollbackfor | 指定哪些异常不触发回滚 | 无 |
isolation | 事务隔离级别(控制并发数据可见性) | isolation.default(数据库默认) |
propagation | 事务传播行为(控制嵌套方法的事务归属) | propagation.required |
readonly | 是否为只读事务(优化性能,适合查询操作) | false |
timeout | 事务超时时间(秒),超时后自动回滚 | -1(无超时) |
value/transactionmanager | 指定事务管理器(多数据源时使用) | 默认事务管理器 |
1. 异常回滚配置(rollbackfor/norollbackfor)
- 默认规则:仅对 runtimeexception 及其子类(非检查异常)回滚,对 exception 及其子类(检查异常)不回滚。
- 推荐配置:rollbackfor = exception.class,确保所有异常都触发回滚:
示例:
@transactional(rollbackfor = exception.class) // 所有异常都回滚 public void savedata() throws exception { // 业务逻辑 }
- 特殊需求:指定不回滚的异常:
示例:
@transactional(norollbackfor = arithmeticexception.class) public void process() { // 发生 arithmeticexception 不回滚,其他异常正常回滚 }
2. 隔离级别(isolation)
控制并发事务之间的数据可见性,可选值:
- isolation.default:使用数据库默认隔离级别(推荐)
- isolation.read_uncommitted:读未提交(最低隔离,可能脏读)
- isolation.read_committed:读已提交(避免脏读,多数数据库默认)
- isolation.repeatable_read:可重复读(避免脏读、不可重复读,mysql 默认)
- isolation.serializable:串行化(最高隔离,避免幻读,性能最低)
示例:
@transactional(isolation = isolation.read_committed) public void querydata() { // 事务内查询,只能看到已提交的数据 }
3.脏读(dirty read)
定义:一个事务读取到了另一个未提交事务修改的数据。
场景示例:
- 事务 a:更新了一条记录但未提交
- 事务 b:读取了事务 a 更新后的记录
- 事务 a:由于某种原因回滚了操作
- 结果:事务 b 读取到的数据是 “无效” 的(已被回滚)
危害:导致读取到临时且可能被撤销的数据,影响业务逻辑正确性。
避免方式:将事务隔离级别设置为 read committed 或更高(repeatable read、serializable)。
mysql 默认隔离级别是 repeatable read,已避免脏读。
4.幻读(phantom read)
定义:一个事务在两次查询中得到不同的结果集,因为另一个事务在两次查询之间插入或删除了数据。
场景示例:
- 事务 a:查询年龄 > 20 的用户,得到 10 条记录
- 事务 b:插入了一条年龄 = 25 的新用户并提交
- 事务 a:再次执行相同查询,得到 11 条记录(多了一条 “幻影” 数据)
5. 传播行为(propagation)
控制嵌套方法的事务归属,核心值:
- propagation.required(默认):如果当前有事务,则加入;否则新建事务。
- propagation.requires_new:无论当前是否有事务,都新建独立事务(原事务暂停)。
- propagation.supports:如果当前有事务,则加入;否则以非事务方式执行。
- propagation.not_supported:以非事务方式执行,若当前有事务则暂停。
- propagation.never:必须在非事务环境执行,否则抛异常。
- propagation.mandatory:必须在事务环境执行,否则抛异常。
- propagation.nested:嵌套事务(依赖数据库支持,如 mysql 的 savepoint)。
示例:独立事务场景
@service public class orderservice { @autowired private logservice logservice; @transactional(rollbackfor = exception.class) public void createorder() { // 订单业务(主事务) logservice.savelog(); // 调用日志服务 } }
@service public class logservice { // 日志保存使用独立事务,即使订单回滚,日志仍会提交 @transactional(propagation = propagation.requires_new, rollbackfor = exception.class) public void savelog() { // 保存日志 } }
6. 其他属性
- readonly = true:适用于纯查询方法,数据库可优化性能(如避免写锁):
@transactional(readonly = true) public list<user> queryusers() { return usermapper.selectall(); }
- timeout = 10:事务超时时间 10 秒,超时后自动回滚:
@transactional(timeout = 10) public void longtimetask() { // 长时间任务,超时会回滚 }
三、事务生效条件
1.代理模式:spring 事务基于 aop 代理实现,需满足:
- 方法必须是 public(非 public 方法注解可能失效)。
- 需通过 spring 容器的代理对象调用方法(同类中自调用可能失效,需特殊处理)。
2.异常处理:
- 异常必须抛出到 @transactional 标记的方法外(被捕获的异常不会触发回滚)。
- 需符合 rollbackfor 配置的异常类型。
3.数据库支持:
- 数据库必须支持事务(如 mysql 的 innodb 引擎,myisam 不支持)。
四、常见问题
1.自调用事务失效:
@service public class userservice { public void methoda() { methodb(); // 自调用,事务可能失效 } @transactional public void methodb() { // 业务逻辑 } }
解决:通过 spring 上下文获取代理对象调用,或使用 @enableaspectjautoproxy(exposeproxy = true) 暴露代理。
2.多线程事务不共享:
新线程中的方法不会加入当前事务,需手动协调事务。
3.错误的异常处理:
@transactional public void save() { try { // 业务逻辑 } catch (exception e) { // 异常被捕获,未抛出,事务不会回滚 } }
解决:捕获后需重新抛出异常,或手动调用 transactionstatus.setrollbackonly()。
五、总结
@transactional 是 spring boot 中简化事务管理的强大工具,核心关注:
- 异常回滚规则(rollbackfor)
- 并发控制(isolation)
- 嵌套事务关系(propagation)
合理配置可确保数据一致性,同时需注意代理模式、异常处理等细节避免事务失效。
到此这篇关于springboot 中的 @transactional 事务详解及注意事项的文章就介绍到这了,更多相关springboot @transactional 事务内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论