springboot中的事务请给我详细介绍

下面这份内容从你作为 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% 的“事务失败”都不是数据库回滚,而是消息与数据库不一致