Spring 事务
事务基础
@Transactional 注解
@Service
public class UserService {
@Transactional
public void createUser(User user) {
userDao.insert(user);
}
}注解属性
| 属性 | 说明 | 默认值 |
|---|---|---|
value / transactionManager | 事务管理器名称 | 默认事务管理器 |
propagation | 传播行为 | REQUIRED |
isolation | 隔离级别 | 数据库默认 |
timeout | 超时时间(秒) | -1(不超时) |
readOnly | 是否只读 | false |
rollbackFor | 回滚异常类型 | RuntimeException |
noRollbackFor | 不回滚异常类型 | 无 |
事务传播行为
七种传播行为
| 传播行为 | 说明 | 使用场景 |
|---|---|---|
| REQUIRED(默认) | 有事务就加入,没有就新建 | 大多数场景 |
| REQUIRES_NEW | 总是新建事务,挂起当前事务 | 日志记录、独立操作 |
| SUPPORTS | 有事务就加入,没有就以非事务执行 | 查询操作 |
| NOT_SUPPORTED | 以非事务执行,挂起当前事务 | 不需要事务的操作 |
| MANDATORY | 必须在事务中调用,否则抛异常 | 强制要求事务 |
| NEVER | 不能在事务中调用,否则抛异常 | 禁止事务 |
| NESTED | 嵌套事务(保存点) | 部分回滚场景 |
图解传播行为
REQUIRED(默认)
┌─────────────────────────────────┐
│ 事务A │
│ ├── methodA() │
│ │ └── methodB() ← 加入事务A │
└─────────────────────────────────┘
REQUIRES_NEW
┌─────────────────────────────────┐
│ 事务A │
│ ├── methodA() │
│ │ └── 【挂起事务A】 │
│ │ ┌─────────────────┐ │
│ │ │ 事务B(新建) │ │
│ │ │ └── methodB() │ │
│ │ └─────────────────┘ │
│ │ ┌── 【恢复事务A】 │
└─────────────────────────────────┘
NESTED
┌─────────────────────────────────┐
│ 事务A │
│ ├── methodA() │
│ │ └── Savepoint sp1 │
│ │ ┌─────────────────┐ │
│ │ │ 嵌套事务 │ │
│ │ │ └── methodB() │ │
│ │ └─────────────────┘ │
│ │ ← 可以回滚到 sp1 │
└─────────────────────────────────┘代码示例
@Service
public class UserService {
@Autowired
private LogService logService;
// REQUIRED:默认,加入现有事务
@Transactional(propagation = Propagation.REQUIRED)
public void createUser(User user) {
userDao.insert(user);
logService.log("创建用户"); // 加入当前事务
}
// REQUIRES_NEW:总是新建事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void log(String message) {
// 独立事务,即使主事务回滚,日志也会保存
logDao.insert(message);
}
// NESTED:嵌套事务
@Transactional(propagation = Propagation.NESTED)
public void batchCreate(List<User> users) {
for (User user : users) {
try {
userDao.insert(user);
} catch (Exception e) {
// 回滚到保存点,继续处理其他用户
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}
}事务隔离级别
@Transactional(isolation = Isolation.READ_COMMITTED)
public User getUser(Long id) {
return userDao.findById(id);
}| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| DEFAULT | 使用数据库默认 | - | - |
| READ_UNCOMMITTED | √ | √ | √ |
| READ_COMMITTED | × | √ | √ |
| REPEATABLE_READ | × | × | √* |
| SERIALIZABLE | × | × | × |
事务实现原理
AOP 代理实现
@Transactional 方法调用:
1. 代理对象拦截调用
2. 事务切面开启事务
3. 执行目标方法
4. 成功 → 提交事务
失败 → 回滚事务核心类
| 类 | 作用 |
|---|---|
TransactionInterceptor | 事务拦截器 |
TransactionAspectSupport | 事务切面支持 |
AbstractPlatformTransactionManager | 事务管理器抽象类 |
DataSourceTransactionManager | JDBC 事务管理器 |
JpaTransactionManager | JPA 事务管理器 |
事务执行流程
// TransactionInterceptor 核心逻辑
public Object invoke(MethodInvocation invocation) throws Throwable {
// 1. 获取事务属性
TransactionAttribute txAttr = getTransactionAttribute();
// 2. 获取事务管理器
PlatformTransactionManager tm = getTransactionManager();
// 3. 开启事务
TransactionInfo txInfo = tm.getTransaction(txAttr);
try {
// 4. 执行目标方法
Object result = invocation.proceed();
// 5. 提交事务
tm.commit(txInfo);
return result;
} catch (Exception ex) {
// 6. 回滚事务
tm.rollback(txInfo);
throw ex;
}
}事务失效场景
1. 方法非 public
// 失效:private 方法
@Transactional
private void createUser() { }
// 原因:Spring AOP 基于代理,private 方法无法被代理2. 同类方法调用
@Service
public class UserService {
public void methodA() {
methodB(); // 直接调用,不走代理,事务失效
}
@Transactional
public void methodB() {
// ...
}
}
// 解决方案:
// 1. 注入自己
@Autowired
private UserService self;
public void methodA() {
self.methodB(); // 走代理
}
// 2. 使用 AopContext
public void methodA() {
((UserService) AopContext.currentProxy()).methodB();
}3. 异常被捕获
@Transactional
public void createUser(User user) {
try {
userDao.insert(user);
throw new RuntimeException("异常");
} catch (Exception e) {
// 异常被捕获,事务不会回滚
log.error("创建失败", e);
}
}
// 解决:手动回滚
@Transactional
public void createUser(User user) {
try {
userDao.insert(user);
throw new RuntimeException("异常");
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}4. 异常类型不匹配
// 默认只回滚 RuntimeException 和 Error
@Transactional
public void createUser() throws Exception {
throw new Exception("受检异常"); // 不会回滚
}
// 解决:指定回滚异常类型
@Transactional(rollbackFor = Exception.class)
public void createUser() throws Exception {
throw new Exception("受检异常"); // 会回滚
}5. 数据库引擎不支持事务
-- MySQL MyISAM 不支持事务
CREATE TABLE user (...) ENGINE=MyISAM;
-- 使用 InnoDB
CREATE TABLE user (...) ENGINE=InnoDB;6. 类没有被 Spring 管理
// 失效:没有 @Service/@Component 等注解
public class UserService {
@Transactional
public void createUser() { }
}只读事务
@Transactional(readOnly = true)
public User getUser(Long id) {
return userDao.findById(id);
}优势:
- 数据库可以进行优化(不获取写锁)
- Hibernate 不进行脏检查
- 提高查询性能
注意: 只读事务中进行写操作会抛异常。
事务超时
@Transactional(timeout = 5) // 5秒超时
public void longRunningOperation() {
// 超过5秒自动回滚
}多事务管理器
@Configuration
public class DataSourceConfig {
@Bean
@Primary
public PlatformTransactionManager primaryTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public PlatformTransactionManager secondaryTransactionManager(@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// 使用指定事务管理器
@Transactional("secondaryTransactionManager")
public void useSecondaryDataSource() {
// ...
}编程式事务
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void createUser(User user) {
transactionTemplate.execute(status -> {
userDao.insert(user);
return null;
});
}
// 或者直接使用事务管理器
@Autowired
private PlatformTransactionManager transactionManager;
public void createUser2(User user) {
TransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
userDao.insert(user);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}面试高频问题
Q1: Spring 事务的实现原理?
回答要点:
- AOP 代理:通过动态代理拦截方法调用
- TransactionInterceptor:事务拦截器处理事务逻辑
- PlatformTransactionManager:事务管理器执行具体操作
- ThreadLocal:存储当前线程的事务信息(连接)
Q2: REQUIRED 和 REQUIRES_NEW 的区别?
| 对比 | REQUIRED | REQUIRES_NEW |
|---|---|---|
| 事务数量 | 可能共用一个 | 总是新建 |
| 回滚影响 | 整个事务回滚 | 独立回滚 |
| 性能 | 更好 | 有额外开销 |
Q3: 事务什么时候会失效?
- 方法非 public
- 同类方法调用(不走代理)
- 异常被捕获未抛出
- 异常类型不匹配(默认只回滚 RuntimeException)
- 数据库不支持事务(MyISAM)
- 类未被 Spring 管理
Q4: @Transactional 如何保证线程安全?
回答:通过 ThreadLocal
每个线程有自己的数据库连接,存储在 ThreadLocal 中,互不干扰。
// TransactionSynchronizationManager 源码
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");Q5: 嵌套事务和 REQUIRES_NEW 的区别?
| 对比 | NESTED | REQUIRES_NEW |
|---|---|---|
| 事务关系 | 父子关系 | 独立事务 |
| 回滚影响 | 子回滚不影响父 | 完全独立 |
| 实现方式 | 保存点(Savepoint) | 挂起当前事务 |
| 数据库要求 | 需要支持保存点 | 无特殊要求 |
总结
Spring 事务核心要点:
1. 注解:@Transactional 及其属性
2. 传播行为:7种,REQUIRED 默认,REQUIRES_NEW 常用
3. 隔离级别:与数据库一致
4. 失效场景:非 public、同类调用、异常处理、引擎不支持
5. 实现原理:AOP + ThreadLocal + PlatformTransactionManager