下面这份内容从你作为 Java 后端工程师真正会踩的坑与底层原理出发,系统地讲清楚 Spring Boot 中的事务模型,而不是停留在“加个 @Transactional 就完事了”的层面。
⸻
一、Spring 事务的本质是什么?
Spring 事务本质上是:
用 AOP 在方法调用前后,帮你自动管理 JDBC 事务边界
它不是数据库事务
它是:对数据库事务的编排器
你写的是:
@Transactional
public void createOrder() {
insertOrder();
insertOrderItem();
}
Spring 实际做的是:
conn = DataSource.getConnection()
conn.setAutoCommit(false)
try {
insertOrder()
insertOrderItem()
conn.commit()
} catch (Exception e) {
conn.rollback()
}
⸻
二、Spring Boot 默认事务模型
Spring Boot 里,只要你引入了:
spring-boot-starter-jdbc
spring-boot-starter-data-jpa
mybatis-spring-boot-starter
就自动启用:
DataSourceTransactionManager
也就是说:
Spring Boot 的事务 = JDBC 本地事务(不是分布式事务)
⸻
三、@Transactional 是如何生效的?
它不是魔法,是 AOP 代理
Spring 启动时:
@Transactional
↓
BeanPostProcessor
↓
给这个 Bean 生成代理对象
你真正调用的是:
OrderServiceProxy.createOrder()
流程:
Proxy
→ 开启事务
→ 调用真实 OrderService.createOrder()
→ 提交 or 回滚
⸻
四、为什么“同一个类里调用事务会失效”?
这是 90% 工程师踩过的坑
@Service
public class OrderService {
public void placeOrder() {
this.createOrder(); // ❌ 事务失效
}
@Transactional
public void createOrder() {
...
}
}
因为:
this.createOrder()
绕过了代理对象
等于绕过了事务拦截器
正确方式:
@Autowired
private OrderService self;
self.createOrder(); // 通过代理调用
⸻
五、事务传播行为(Propagation)
这是 Spring 事务的灵魂
传播级别 含义
REQUIRED 没事务就新建,有就加入(默认)
REQUIRES_NEW 一定新建,挂起外层事务
NESTED 子事务(基于 savepoint)
SUPPORTS 有就用,没有就不用
NOT_SUPPORTED 强制不用事务
NEVER 有事务直接报错
⸻
经典场景
@Transactional
public void placeOrder() {
saveOrder(); // 主事务
logService.log(); // 要求无论如何都成功
}
@Transactional(propagation = REQUIRES_NEW)
public void log() {
insert_log();
}
即使 placeOrder 回滚
log 也会提交
⸻
六、Spring 什么时候会回滚?
默认规则:
只对 RuntimeException 和 Error 回滚
不会对:
IOException
SQLException
回滚
所以你必须写:
@Transactional(rollbackFor = Exception.class)
否则:
try {
...
} catch (Exception e) {
// 吞掉异常
}
事务会照样提交 → 严重数据污染
⸻
七、事务和数据库隔离级别的关系
Spring 的事务隔离级别:
@Transactional(isolation = Isolation.READ_COMMITTED)
本质是:
conn.setTransactionIsolation(...)
常用:
级别 问题
READ_COMMITTED 默认,可能不可重复读
REPEATABLE_READ MySQL 默认
SERIALIZABLE 性能极差
Spring 不会解决幻读、锁问题
它只是把你配置传给数据库
⸻
八、事务 ≠ 线程安全
事务是:
数据库连接级别
你在异步线程:
@Transactional
public void pay() {
asyncExecutor.submit(() -> updateAccount());
}
updateAccount 根本不在事务里
⸻
九、Kafka、MQ、RPC 与事务的关系
Spring 事务只能管:
同一个 DataSource
它无法保证:
• 发 MQ
• 调用 RPC
• 调 Redis
与你的数据库原子性一致
这就是为什么你需要:
• 事务消息
• Outbox 表
• 本地消息表
⸻
十、你作为后端工程师必须掌握的事务设计铁律
如果你记住 3 条就够了:
1. 事务边界一定在 Service 层
Controller 不要加事务
Mapper 不要加事务
⸻
2. 不要在事务里写远程调用
RPC、Kafka、HTTP 都会导致:
• 长事务
• 锁膨胀
• 死锁
⸻
3. 事务只保证数据库一致性,不保证业务正确性
你必须设计:
• 幂等
• 补偿
• 状态机
⸻
如果你愿意,我可以下一步结合你“Kafka + Spring Boot”的背景,给你讲:
为什么分布式系统里,99% 的“事务失败”都不是数据库回滚,而是消息与数据库不一致