事务核心目标:保证一组数据库操作,要么全部成功,要么全部失败回滚。Java事务分为:JDBC原生事务、JTA分布式事务、Spring声明式/编程式事务,包含传播行为、隔离级别、回滚规则、失效场景,全部以表格结构化整理。
本质:数据库原生有事务能力,但裸用 JDBC 写事务代码极其繁琐;Java 事务管理(尤其 Spring 事务)是对数据库事务的高层封装,解决业务系统数据一致性问题。
Java事务管理是对数据库事务能力的高层封装,核心是保障一组数据库操作原子执行,要么全部成功提交,要么失败整体回滚,维护数据ACID特性。原生JDBC需手动编写提交、回滚样板代码,开发繁琐易出错。Spring事务通过AOP与@Transactional注解简化开发,提供传播行为、隔离级别、异常回滚、超时等能力,自动管理数据库连接资源。区分本地事务与分布式事务,解决方法嵌套调用、并发业务的数据一致性,避免出现扣钱不加账这类脏数据,是业务系统保障数据可靠的基础组件。
一、事务基础概念
ACID四大特性
| 特性 | 全称 | 说明 |
|---|---|---|
| A | Atomicity 原子性 | 事务内操作不可分割,全部成功或全部回滚 |
| C | Consistency 一致性 | 事务前后业务数据状态合法,由原子+隔离+持久共同保障 |
| I | Isolation 隔离性 | 多个事务并发,互相之间数据不可见,防止脏读、不可重复读、幻读 |
| D | Durability 持久性 | 事务提交后,修改永久落地数据库,宕机不丢失 |
并发事务3大问题
| 问题 | 描述 |
|---|---|
| 脏读 | A事务读到B事务未提交的数据,如果B回滚,读到脏无效数据 |
| 不可重复读 | 同一事务内,同一行数据两次查询结果不一样,被其他事务提交修改 |
| 幻读 | 同一事务,同样条件查询,前后返回行数变化,其他事务插入/删除数据 |
数据库隔离级别(MySQL InnoDB)
| 隔离级别 | 脏读 | 不可重复读 | 幻读 | MySQL默认 |
|---|---|---|---|---|
| READ UNCOMMITTED 读未提交 | ✅发生 | ✅发生 | ✅发生 | 极少使用 |
| READ COMMITTED 读已提交(RC) | ❌避免 | ✅发生 | ✅发生 | Oracle默认 |
| REPEATABLE READ 可重复读(RR) | ❌避免 | ❌避免 | ✅(InnoDB MVCC规避) | ✅MySQL InnoDB默认 |
| SERIALIZABLE 串行化 | ❌避免 | ❌避免 | ❌避免 | 性能极低,几乎不用 |
InnoDB 在 RR级别通过MVCC解决幻读快照读;当前读(select ... for update)会通过临键锁解决幻读。
二、Java 三套事务体系对比
| 事务体系 | 适用场景 | API入口 | 是否依赖容器 | 特点 |
|---|---|---|---|---|
| JDBC原生事务 | 单库本地事务 | Connection |
否,纯JDBC | 底层原始,手动编码;关闭自动提交setAutoCommit(false) |
| JTA(Java Transaction API) | 分布式多数据源事务 | UserTransaction |
需要容器(Tomcat/JBoss)或Atomikos等第三方 | XA二阶段提交,性能差,开发繁琐 |
| Spring事务 | 绝大多数业务,单库为主,也可集成JTA | @Transactional / TransactionTemplate |
Spring环境 | 封装JDBC/JTA,分为编程式、声明式;开发主流 |
2.1 JDBC原生事务核心代码要点
Connection关键方法:
| 方法 | 作用 |
|---|---|
connection.setAutoCommit(false) |
关闭自动提交,开启事务 |
connection.commit() |
手动提交事务 |
connection.rollback() |
回滚事务 |
connection.setSavepoint() |
设置保存点,支持局部回滚 |
缺陷:每个业务都要写try‑catch‑commit‑rollback样板代码;多数据源无法支持;没有传播行为。
2.2 JTA 分布式事务
JTA:Java规范,定义事务接口;底层实现XA协议2PC
角色:事务管理器TM、资源管理器RM(各个数据库)
| 对象 | 作用 |
|---|---|
javax.transaction.UserTransaction |
用户操作事务:begin/commit/rollback |
javax.transaction.TransactionManager |
底层事务管理器 |
| XAResource | 每个数据库资源参与分布式事务接口 |
缺点:性能损耗大;部分数据库驱动对XA支持一般;现在业务更多用Seata替代JTA。
三、Spring事务两大模式:编程式 vs 声明式
| 模式 | 实现方式 | 核心API | 优点 | 缺点 |
|---|---|---|---|---|
| 编程式事务 | 硬编码写在业务代码 | TransactionTemplate / PlatformTransactionManager |
细粒度控制,灵活;不受AOP限制 | 侵入业务代码,重复样板代码 |
| 声明式事务 | AOP代理,注解@Transactional |
@Transactional注解 |
无侵入业务,简洁,开发主流 | 依赖SpringAOP代理;存在大量失效坑 |
Spring事务底层依然是封装JDBC Connection或者JTA。
Spring核心事务管理器接口
PlatformTransactionManager 顶层接口
| 实现类 | 场景 |
|---|---|
DataSourceTransactionManager |
JDBC/MyBatis,单数据源本地事务,最常用 |
JpaTransactionManager |
JPA/Hibernate |
JtaTransactionManager |
JTA分布式XA事务 |
接口方法:
// 获取事务状态
TransactionStatus getTransaction(TransactionDefinition definition);
// 提交
void commit(TransactionStatus status);
// 回滚
void rollback(TransactionStatus status);
TransactionDefinition:事务定义信息
包含:传播行为、隔离级别、超时时间、是否只读。
四、Spring 7种事务传播行为(重点)
传播行为:一个已经存在事务的方法,调用另一个带事务的方法,事务如何共用/新建。
| 传播行为 | 常量名称 | 说明 |
|---|---|---|
| REQUIRED | PROPAGATION_REQUIRED | ✅默认。有事务就加入当前事务;没有就新建事务。 |
| SUPPORTS | PROPAGATION_SUPPORTS | 有事务就加入;没有就非事务执行。 |
| MANDATORY | PROPAGATION_MANDATORY | 必须已有事务,没有则抛异常。 |
| REQUIRES_NEW | PROPAGATION_REQUIRES_NEW | 总是新建独立事务,挂起外层事务;内层回滚不影响外层。 |
| NOT_SUPPORTED | PROPAGATION_NOT_SUPPORTED | 挂起当前事务,以非事务运行。 |
| NEVER | PROPAGATION_NEVER | 禁止存在事务,已有事务直接抛异常。 |
| NESTED | PROPAGATION_NESTED | 嵌套事务;底层数据库保存点;外层回滚内层跟着回滚;内层回滚不影响外层。仅DataSourceTransactionManager支持。 |
面试高频区分:
REQUIRES_NEWvsNESTEDREQUIRES_NEW:完全独立两个事务;
NESTED:同一个事务,依靠savepoint保存点实现嵌套。
五、@Transactional注解完整属性表
| 属性 | 作用 | 取值说明 |
|---|---|---|
| propagation | 传播行为 | Propagation.REQUIRED / REQUIRES_NEW等7种 |
| isolation | 隔离级别 | Isolation.READ_COMMITTED、REPEATABLE_READ... |
| timeout | 事务超时秒数 | 默认‑1不超时;超时自动回滚 |
| readOnly | 是否只读 | true:优化,禁止写操作;不是强制校验,给数据库提示 |
| rollbackFor | 指定异常触发回滚 | Class数组;rollbackFor = Exception.class |
| rollbackForClassName | 异常全限定名,字符串形式 | 用于注解配置无法直接引入类 |
| noRollbackFor | 指定异常不回滚 | 发生该异常,事务依然提交 |
| noRollbackForClassName | 字符串版本noRollbackFor |
⚠️ 默认规则:Spring声明式事务仅对RuntimeException、Error才回滚;普通受检Exception不会回滚! 捕获异常不抛出也不会回滚。
六、Spring事务常见失效场景(高频面试)
| 失效场景 | 现象 | 原因 |
|---|---|---|
1.非public方法上加@Transactional |
事务不生效 | SpringAOP只能拦截public;private/protected/default无法生成代理 |
| 2.同类内部方法调用 | this.xxx()调用本类注解方法,事务失效 |
this是原始对象,不走AOP代理对象;AOP只有外部调用代理对象才生效 |
| 3.异常被try‑catch捕获,没有抛出 | 异常吞掉,事务不会回滚 | SpringAOP通过捕获方法抛出异常触发回滚;catch住不抛出感知不到异常 |
| 4.数据库引擎不支持事务(MyISAM) | 完全没有事务能力 | MyISAM不支持事务,要InnoDB |
| 5.传播行为配置错误 | 内层异常外层不回滚或者不开启事务 | 例如SUPPORTS/NOT_SUPPORTED等 |
| 6.多数据源,使用普通DataSourceTransactionManager | 只能单库生效,跨库不具备原子性 | 本地事务只绑定一个Connection,跨库需要分布式事务JTA/Seata |
| 7.多线程环境调用事务方法 | 子线程无法继承主线程事务 | Spring事务绑定ThreadLocal,不同线程ThreadLocal隔离;子线程会新建事务 |
| 8.手动设置readOnly=true却做写操作 | 部分数据库抛异常,部分数据库仅提示 | readOnly只是优化hint,不是强制拦截写SQL |
| 9.自己new对象,不用Spring容器获取Bean | 注解完全无效 | 没有被SpringAOP代理,是原生对象 |
同类调用解决方案:1.注入自身代理;2.拆分到不同Service;3.通过AopContext获取代理对象。
七、Spring事务底层原理
1、Spring启动扫描
@Transactional,生成AOP代理对象(JDK动态代理 / CGLIB)2、调用代理对象方法,进入
TransactionInterceptor拦截器
3、TransactionDefinition解析注解配置,交给PlatformTransactionManager4、从ThreadLocal获取/创建数据库Connection,关闭autoCommit
5、执行业务逻辑;方法抛出异常:执行rollback;正常结束执行commit
6、Connection归还连接池。
关键点:Connection绑定到当前线程ThreadLocal,同一个线程内多个事务方法拿到同一个数据库连接。
| 关键点 | 说明 |
|---|---|
| Connection绑定 | TransactionSynchronizationManager ThreadLocal保存Connection资源 |
| 代理方式 | 实现接口用JDK动态代理;类无接口使用CGLIB代理 |
| 回滚标记 | TransactionStatus.setRollbackOnly()标记回滚,即使不抛异常也回滚 |
手动触发回滚(编程式)
TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
status.setRollbackOnly();
八、本地事务 vs 分布式事务对比
| 维度 | Spring本地事务(DataSourceTransactionManager) | 分布式事务(JTA/Seata) |
|---|---|---|
| 数据库数量 | 单数据源,一个数据库连接 | 多个数据库、多个微服务 |
| 底层原理 | JDBC Connection事务 | XA‑2PC / TCC / AT / SAGA |
| 性能 | 几乎无额外损耗 | 开销大,AT模式存在undo_log日志 |
| 实现难度 | 简单,@Transactional直接使用 | 复杂,需要额外组件 |
| 数据一致性 | 强一致性 | AT模式最终一致性;XA强一致 |
企业现状:JTA XA性能差,新项目一般不用;微服务分布式事务主流使用 Seata AT模式。
九、最佳实践总结表格
| 实践 | 建议 |
|---|---|
| 注解位置 | @Transactional写在public方法;推荐写在Service层,不要写Controller |
| 回滚配置 | 业务上建议rollbackFor = Exception.class,避免受检异常不回滚坑 |
| 避免同类内部调用 | 业务拆分不同Service,不要this调用 |
| 大事务 | 尽量缩小事务范围;避免把RPC、大查询放入事务,防止长事务锁表、数据库连接耗尽 |
| 多线程 | 不要在子线程执行数据库事务;子线程无法继承父线程事务 |
| 只读查询 | 查询方法设置readOnly=true,给底层驱动优化提示 |
| 异常处理 | 如果需要catch部分异常,catch后手动设置回滚标记或者抛出异常 |
常见坑速记
@Transactional不是万能,依赖AOP代理+public+抛出异常;- Spring默认只回滚RuntimeException/Error;
- ThreadLocal绑定Connection,跨线程事务失效;
- 单库用
DataSourceTransactionManager;跨库微服务不要指望@Transactional解决,要用分布式事务方案。


