事务管理
问题
Spring 事务的实现原理是什么?@Transactional 的传播行为有哪些?事务在哪些场景下会失效?
答案
Spring 事务的实现原理
Spring 事务基于 AOP 实现:@Transactional 注解的方法会被 TransactionInterceptor 增强,通过代理在方法前后管理事务的开启、提交和回滚。
核心组件:
- PlatformTransactionManager:事务管理器接口(DataSourceTransactionManager、JpaTransactionManager)
- TransactionDefinition:事务定义(传播行为、隔离级别、超时等)
- TransactionStatus:事务状态(是否新事务、是否已完成)
@Transactional 使用
@Service
public class OrderService {
@Transactional // 默认配置
public void createOrder(Order order) {
orderDao.insert(order);
inventoryService.deduct(order.getProductId(), order.getQuantity());
// 如果这里抛出 RuntimeException,两个操作都会回滚
}
@Transactional(
propagation = Propagation.REQUIRED, // 传播行为
isolation = Isolation.READ_COMMITTED, // 隔离级别
timeout = 30, // 超时(秒)
readOnly = false, // 是否只读
rollbackFor = Exception.class, // 回滚异常类型
noRollbackFor = BusinessException.class // 不回滚的异常
)
public void complexOperation() { /* ... */ }
}
七大传播行为
| 传播行为 | 说明 | 使用场景 |
|---|---|---|
| REQUIRED(默认) | 有事务加入,无事务新建 | 大多数场景 |
| REQUIRES_NEW | 无论如何新建事务,原事务挂起 | 独立事务(如日志记录) |
| NESTED | 存在事务则嵌套(保存点),无事务则新建 | 子操作失败不影响父事务 |
| SUPPORTS | 有事务加入,无事务非事务执行 | 查询方法 |
| NOT_SUPPORTED | 非事务执行,存在事务则挂起 | 某些不需要事务的操作 |
| MANDATORY | 必须在事务中调用,否则抛异常 | 强制要求事务的方法 |
| NEVER | 非事务执行,存在事务则抛异常 | 禁止事务的方法 |
重点掌握前三种:
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder() {
orderDao.insert(order);
// REQUIRED:logService 加入当前事务
// REQUIRES_NEW:logService 开启新事务
// NESTED:logService 在保存点内执行
logService.log("创建订单");
}
}
@Service
public class LogService {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void log(String msg) {
// 即使 createOrder 回滚,日志记录不受影响
logDao.insert(msg);
}
}
- REQUIRES_NEW:创建完全独立的新事务,外部事务回滚不影响内部事务
- NESTED:嵌套事务(Savepoint),外部事务回滚会连带回滚嵌套事务,但内部事务回滚不影响外部事务
事务失效的常见场景
1. 自调用(最常见)
同一类中方法 A 调用方法 B,B 的 @Transactional 不生效(因为绕过了代理):
@Service
public class UserService {
public void methodA() {
this.methodB(); // ❌ 自调用,不走代理
}
@Transactional
public void methodB() { /* ... */ }
}
2. 方法不是 public
Spring AOP 默认只代理 public 方法:
@Transactional
private void method() { } // ❌ private 方法不会被代理
3. 异常被吞掉
@Transactional
public void method() {
try {
// 业务操作
} catch (Exception e) {
log.error("出错了", e);
// ❌ 异常被 catch 了,Spring 感知不到,不会回滚
}
}
4. 抛出的异常类型不对
@Transactional // 默认只回滚 RuntimeException 和 Error
public void method() throws Exception {
throw new IOException("IO 异常"); // ❌ 受检异常不会触发回滚
}
// 解决:指定 rollbackFor
@Transactional(rollbackFor = Exception.class)
5. 数据库引擎不支持事务
MySQL 的 MyISAM 引擎不支持事务,需要使用 InnoDB。
6. 没有被 Spring 管理
Bean 没有被 Spring 管理(没有 @Service/@Component 注解),事务不生效。
7. 多线程调用
事务绑定在 ThreadLocal 中,新线程不在同一个事务中。
编程式事务
除了声明式(@Transactional),还可以使用 TransactionTemplate 编程式管理事务:
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void createUser(User user) {
transactionTemplate.execute(status -> {
try {
userDao.insert(user);
roleDao.bindRole(user.getId(), "USER");
return true;
} catch (Exception e) {
status.setRollbackOnly(); // 手动标记回滚
throw e;
}
});
}
}
编程式事务更灵活,适合需要精细控制事务边界的场景。
常见面试问题
Q1: Spring 事务的实现原理?
答案:
基于 AOP 动态代理。@Transactional 注解的类/方法会被 TransactionInterceptor 拦截,在方法执行前通过 PlatformTransactionManager 开启事务,正常返回时提交,异常时回滚。
底层连接管理通过 ThreadLocal 绑定到当前线程,保证同一事务中的多个 DAO 操作使用同一个数据库连接。
Q2: @Transactional 的传播行为有哪些?常用的是哪几个?
答案:
7 种传播行为,常用 3 种:
- REQUIRED(默认):有则加入,无则新建
- REQUIRES_NEW:无论如何新建事务,独立于外部事务
- NESTED:嵌套事务,通过 Savepoint 实现
Q3: @Transactional 失效的场景?
答案:
- 自调用:同类中方法调用(不走代理)
- 非 public 方法:AOP 不代理非 public 方法
- 异常被 catch:Spring 感知不到异常
- 异常类型不匹配:受检异常默认不回滚(需
rollbackFor = Exception.class) - 非 Spring Bean
- 多线程:新线程不共享事务
Q4: 声明式事务和编程式事务的区别?
答案:
| 对比 | 声明式(@Transactional) | 编程式(TransactionTemplate) |
|---|---|---|
| 实现 | AOP 代理 | 代码中手动控制 |
| 侵入性 | 低(注解即可) | 高(需要写代码) |
| 粒度 | 方法级别 | 代码块级别(更细) |
| 灵活性 | 低 | 高 |
大多数场景用声明式,需要精细控制时用编程式。
Q5: @Transactional(readOnly = true) 有什么用?
答案:
标记事务为只读,数据库和 ORM 框架可以做优化:
- 数据库可能使用只读事务优化(不加写锁)
- Hibernate/JPA 不做脏检查(flush 时不检查变更)
- 主从架构中可能路由到从库
通常用于纯查询方法,提升性能。
相关链接
- Spring Transaction 官方文档
- AOP 面向切面编程 - 事务的底层实现基础
- 事务与并发控制 - MySQL 事务隔离级别
- ThreadLocal - 事务连接的线程绑定