1. 事务概述
事务(Transaction)是一组操作的执行单元,具备 ACID 四大特性:
- 原子性(Atomicity):全部成功或全部失败
- 一致性(Consistency):数据状态保持合法
- 隔离性(Isolation):并发事务互不干扰
- 持久性(Durability):提交后永久生效
在企业级应用中,我们通常使用 Spring 事务管理 来简化事务控制,它屏蔽了底层持久化技术(JDBC、Hibernate、JPA 等)的差异,提供统一的事务抽象。
2. Spring 事务管理基础
Spring 提供两种事务管理方式:
2.1 编程式事务管理
通过 TransactionTemplate 或 PlatformTransactionManager 手动控制事务的提交与回滚。
@Service
public class UserService {
@Autowired
private TransactionTemplate transactionTemplate;
public void updateUser() {
transactionTemplate.execute(status -> {
try {
// 业务操作
userDao.update(user);
orderDao.save(order);
return Boolean.TRUE;
} catch (Exception e) {
status.setRollbackOnly(); // 标记回滚
return Boolean.FALSE;
}
});
}
}
优点:精细控制
缺点:侵入性强,代码冗余,不推荐在业务层大量使用。
2.2 声明式事务管理(推荐)
基于 AOP 实现,通过 @Transactional 注解或 XML 配置,无侵入地将事务织入方法。
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
@Autowired
private UserDao userDao;
public void register(User user) {
userDao.insert(user);
// 其他业务操作
}
}
使用要点:
- 在 Spring Boot 中,引入
spring-boot-starter-jdbc或spring-boot-starter-data-jpa后,事务管理自动配置。 - 需要在启动类或配置类上添加
@EnableTransactionManagement(Spring Boot 默认开启)。 - 通常将
@Transactional标注在 Service 层方法 或 类 上。
3. 事务属性详解(进阶)
@Transactional 注解提供了丰富的属性,用于定制事务行为。
3.1 传播行为(Propagation)
定义了事务方法被另一个事务方法调用时,如何传播事务边界。
| 传播行为 | 含义 |
|---|---|
| REQUIRED(默认) | 当前存在事务则加入,否则新建事务 |
| REQUIRES_NEW | 始终新建事务,挂起当前事务(如果存在) |
| NESTED | 当前存在事务则嵌套执行,否则新建(依赖 JDBC savepoint) |
| SUPPORTS | 当前存在事务则加入,否则以非事务方式执行 |
| NOT_SUPPORTED | 以非事务方式执行,挂起当前事务 |
| MANDATORY | 必须存在事务,否则抛异常 |
| NEVER | 必须不存在事务,否则抛异常 |
使用示例
@Service
public class OrderService {
@Transactional(propagation = Propagation.REQUIRED)
public void createOrder(Order order) {
// 加入当前事务
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logAudit(String message) {
// 始终新开事务,不受外部事务回滚影响
}
}
注意:
REQUIRES_NEW会挂起外部事务,新事务独立提交/回滚。NESTED使用 savepoint,外部事务回滚会回滚嵌套事务,但嵌套事务回滚不影响外部事务(JDBC 3.0 以上支持)。
3.2 隔离级别(Isolation)
解决并发事务带来的脏读、不可重复读、幻读问题。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ_UNCOMMITTED | 是 | 是 | 是 |
| READ_COMMITTED | 否 | 是 | 是 |
| REPEATABLE_READ | 否 | 否 | 是(MySQL InnoDB 默认) |
| SERIALIZABLE | 否 | 否 | 否 |
Spring 中默认使用底层数据库的默认隔离级别(如 MySQL 为 REPEATABLE_READ,Oracle 为 READ_COMMITTED)。
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateStock() { ... }
设置隔离级别需确保数据库支持,且会降低并发性能,按需使用。
3.3 超时(Timeout)
指定事务的最长执行时间(秒),超时则自动回滚。
@Transactional(timeout = 30) // 单位:秒
public void batchProcess() { ... }
- 默认值为 -1(不超时)。
- 超时计时从事务开始到方法结束,若超时抛出
TransactionTimedOutException。
3.4 只读(ReadOnly)
标记事务为只读,有助于数据库优化(如 Hibernate 的 flush 模式),但不强制数据不修改。
@Transactional(readOnly = true)
public List<User> findUsers() { ... }
- 通常用于查询方法。
- 如果只读事务中执行了写操作,某些数据库或 ORM 框架会抛出异常。
3.5 回滚规则(rollbackFor / noRollbackFor)
这是开发中最容易踩坑的属性,务必掌握!
Spring 事务默认的回滚策略:
- 运行时异常(RuntimeException) 和 错误(Error) 发生时,自动回滚。
- 受检异常(Checked Exception,即 Exception 的子类非 RuntimeException) 发生时,不回滚。
由于业务中通常希望任何异常都回滚,我们需要显式配置:
// 对所有 Exception 及其子类回滚(包括受检异常)
@Transactional(rollbackFor = Exception.class)
public void transferMoney() throws Exception { ... }
// 指定多个异常
@Transactional(rollbackFor = {SQLException.class, IOException.class})
public void uploadFile() { ... }
// 不回滚指定的异常(即使它是 RuntimeException)
@Transactional(noRollbackFor = NullPointerException.class)
public void process() { ... }
最佳实践:
建议在 Service 类的 类级别 配置 @Transactional(rollbackFor = Exception.class),统一处理,避免遗漏。
4. 事务管理原理(源码级别)
Spring 声明式事务底层基于 AOP 动态代理(JDK Proxy 或 CGLIB)。核心组件:
- TransactionInterceptor:方法拦截器,负责获取事务属性、开启事务、执行目标方法、提交/回滚。
- PlatformTransactionManager:事务管理器接口,具体实现如
DataSourceTransactionManager(JDBC)、JpaTransactionManager、HibernateTransactionManager。 - TransactionStatus:当前事务状态,包含是否是新事务、是否有 savepoint、是否只回滚等。
简化流程:
- 代理对象调用目标方法。
TransactionInterceptor根据@Transactional属性创建或加入事务。- 执行目标方法(业务逻辑)。
- 若抛出异常且符合回滚规则,则回滚;否则提交。
5. 常见问题与注意事项
5.1 自调用失效
在同一个类中,一个非事务方法调用另一个标注了 @Transactional 的方法,事务不会生效,因为 Spring 的代理无法拦截内部调用。
解决方案:
- 将事务方法移到另一个 Service 中。
- 通过
AopContext.currentProxy()获取代理对象调用自身(需暴露代理)。
@Service
public class UserService {
@Autowired
private UserService self; // 注入自身(Spring 2.6+ 需允许循环依赖)
@Transactional
public void doTransaction() { ... }
public void doNonTransaction() {
// self.doTransaction(); // 通过代理调用
// 或 ((UserService) AopContext.currentProxy()).doTransaction();
}
}
5.2 事务管理器配置
多数据源场景需指定 transactionManager:
@Transactional(transactionManager = "secondaryTransactionManager")
public void saveToSecondaryDb() { ... }
5.3 事务回滚与异常处理
- 事务方法内捕获异常后若不重新抛出,事务会正常提交(不会回滚),需谨慎。
- 推荐在 Service 层向外抛出异常,由上层(如 Controller 或全局异常处理器)统一处理。
@Transactional(rollbackFor = Exception.class)
public void update() {
try {
// 业务操作
} catch (Exception e) {
// 必须重新抛出,否则事务不会回滚
throw new RuntimeException("业务异常", e);
}
}
5.4 只读事务优化
readOnly = true 在底层可能开启数据库只读事务,有助于性能(如 Hibernate 跳过脏检查),但并非所有数据库都支持。
5.5 传播行为与嵌套事务
REQUIRES_NEW会挂起当前事务,消耗数据库连接,慎用。NESTED使用 savepoint,仅当底层支持(如 JDBC 3.0),且PlatformTransactionManager支持嵌套事务(如DataSourceTransactionManager)。
6. 总结
| 属性 | 关键点 |
|---|---|
| rollbackFor | 默认仅回滚 RuntimeException 和 Error;务必设为 Exception.class 以覆盖受检异常 |
| propagation | 默认 REQUIRED,根据业务场景选择合适的传播行为 |
| isolation | 默认使用数据库隔离级别,按需提高 |
| timeout | 防止长事务,设置合理超时 |
| readOnly | 查询方法可开启,提升性能 |
开发准则:
- 事务应加在 Service 层,粒度适中,不宜过大(避免长事务锁表)。
- 类级别统一设置
rollbackFor = Exception.class,避免遗漏。 - 注意自调用失效问题,确保事务通过代理对象执行。
- 单元测试中可通过
@Transactional默认回滚(@Rollback)保证测试数据干净。
Comments NOTHING