目录
步骤 2:配置事务管理器(Spring Boot 自动配置,非 Boot 需手动)
步骤 3:在 Service 方法上添加 @Transactional 注解
4.2 代码示例:使用 TransactionTemplate
五、Spring Boot 事务自动配置:新手也能秒懂的原理
5.3 事务属性配置(application.properties)
6.3 坑 3:未处理的检查型异常(Checked Exception)
6.4 坑 4:自动提交未关闭(@Transactional 失效的隐藏原因)
一、为什么需要事务?从一个真实案例说起
假设你正在开发一个电商系统,用户下单时需要同时完成两个操作:
- 创建订单记录(插入订单表)
- 扣减库存(更新商品表库存字段)
如果没有事务保护,可能会出现以下情况:
- 订单创建成功,但库存扣减失败
- 系统突然断电,导致部分操作未持久化
- 多用户并发下单时,库存出现负数
事务的核心价值:确保这两个操作要么全部成功,要么全部失败,就像一个 "原子操作"。
二、Spring 事务核心概念:先搞懂这三个关键组件
2.1 三大核心接口
(1)PlatformTransactionManager(事务管理器)
- 作用:管理事务的创建、提交、回滚
- 常见实现类:
- DataSourceTransactionManager(用于 JDBC/MyBatis)
- JpaTransactionManager(用于 JPA/Hibernate)
- HibernateTransactionManager(旧版 Hibernate 专用)
- 问题:为什么需要选择不同的实现类?
👉 因为不同持久化框架的事务操作方式不同,比如 JPA 和 MyBatis 的底层连接获取方式有差异
(2)TransactionDefinition(事务定义)
- 作用:定义事务的属性(隔离级别、传播行为、超时时间等)
- 核心属性:
public interface TransactionDefinition {
int getPropagationBehavior(); // 传播行为(7种)
int getIsolationLevel(); // 隔离级别(5种)
int getTimeout(); // 超时时间(秒)
boolean isReadOnly(); // 是否只读事务
}
(3)TransactionStatus(事务状态)
- 作用:保存事务运行时的状态(是否新事务、是否已回滚等)
- 常用方法:
status.isNewTransaction(); // 是否是新创建的事务
status.setRollbackOnly(); // 标记为仅回滚(手动回滚)
三、声明式事务:99% 的场景都用它(附完整代码示例)
3.1 最简入门:3 步搞定基础使用
步骤 1:添加依赖(Maven/Gradle)
<!-- Spring Boot场景 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId> <!-- 包含事务支持 -->
</dependency>
<!-- 非Spring Boot场景需手动添加 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.11</version>
</dependency>
步骤 2:配置事务管理器(Spring Boot 自动配置,非 Boot 需手动)
// Spring Boot自动配置,无需额外代码
// 非Spring Boot场景需在配置类中添加:
@Configuration
public class TransactionConfig {
@Bean
public DataSourceTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
步骤 3:在 Service 方法上添加 @Transactional 注解
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductRepository productRepository;
// 核心业务方法,添加事务注解
@Transactional
public void createOrder(Order order) {
// 1. 创建订单
orderRepository.save(order);
// 2. 扣减库存(假设这里可能抛出异常)
productRepository.decreaseStock(order.getProductId(), order.getQuantity());
}
}
3.2 @Transactional 注解全参数解析
参数名 |
作用 |
默认值 |
示例 |
value |
限定作用的事务管理器 Bean 名称 |
空(使用默认管理器) |
@Transactional("customTransactionManager") |
propagation |
事务传播行为(解决多个事务方法嵌套调用的问题) |
Propagation.REQUIRED |
@Transactional(propagation = Propagation.REQUIRES_NEW) |
isolation |
事务隔离级别(解决并发事务的数据可见性问题) |
Isolation.DEFAULT |
@Transactional(isolation = Isolation.READ_COMMITTED) |
timeout |
事务超时时间(超时则自动回滚) |
-1(永不超时) |
@Transactional(timeout = 10) |
readOnly |
标记为只读事务(优化数据库性能) |
false |
@Transactional(readOnly = true) |
rollbackFor |
指定需要回滚的异常类型(可多个) |
空(仅回滚 RuntimeException) |
@Transactional(rollbackFor = {SQLException.class, IOException.class}) |
rollbackForClassName |
同上,通过异常类名指定(字符串形式) |
空 |
@Transactional(rollbackForClassName = "java.sql.SQLException") |
noRollbackFor |
指定不需要回滚的异常类型(可多个) |
空 |
@Transactional(noRollbackFor = BusinessException.class) |
noRollbackForClassName |
同上,通过异常类名指定(字符串形式) |
空 |
@Transactional(noRollbackForClassName = "com.example.BusinessException") |
传播行为深度解析(最容易混淆的点)
假设存在以下调用关系:
methodA() { ... methodB() ... }
methodA和methodB都添加了 @Transactional 注解,传播行为决定了两者的事务如何关联。
传播行为 |
英文名称 |
核心逻辑 |
典型场景 |
REQUIRED |
必填(默认) |
如果当前有事务,加入该事务;否则创建新事务 |
普通业务方法 |
REQUIRES_NEW |
需要新事务 |
挂起当前事务,创建新事务执行 |
独立于外层的子事务(如日志记录) |
SUPPORTS |
支持事务 |
如果当前有事务,加入该事务;否则以非事务方式执行 |
可选事务的查询方法 |
NOT_SUPPORTED |
不支持事务 |
挂起当前事务,以非事务方式执行 |
性能优先的只读查询 |
MANDATORY |
强制事务 |
必须存在当前事务,否则抛出异常 |
依赖外层事务的子操作 |
NEVER |
禁止事务 |
必须不存在当前事务,否则抛出异常 |
绝对不允许事务的场景 |
NESTED |
嵌套事务 |
在当前事务中创建一个保存点(仅 JDBC 支持,Hibernate 不支持) |
可部分回滚的子操作 |
案例:
外层方法A(REQUIRED)调用内层方法B(REQUIRES_NEW):
- A先创建事务 T1
- B挂起 T1,创建新事务 T2
- 如果B抛出异常,T2 回滚,T1 可选择继续执行或回滚
四、编程式事务:适合复杂场景的精细控制
4.1 什么时候用编程式事务?
- 需要在事务执行过程中动态获取事务状态
- 需要更细粒度的事务控制(如手动设置回滚)
- 非注解场景(如第三方框架集成)
4.2 代码示例:使用 TransactionTemplate
@Service
public class OrderService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private OrderRepository orderRepository;
@Autowired
private ProductRepository productRepository;
public void createOrderWithProgrammaticTransaction(Order order) {
transactionTemplate.execute(status -> { // 传入TransactionCallback
try {
orderRepository.save(order);
productRepository.decreaseStock(order.getProductId(), order.getQuantity());
// 主动提交需返回null或正常对象,异常会触发回滚
return null;
} catch (Exception e) {
// 手动标记回滚(可选,抛出异常也会回滚)
status.setRollbackOnly();
throw new RuntimeException("事务执行失败", e);
}
});
}
}
4.3 对比声明式 vs 编程式
特性 |
声明式(@Transactional) |
编程式(TransactionTemplate) |
学习成本 |
低(简单注解) |
中(需要理解 TransactionCallback) |
灵活性 |
低(固定流程) |
高(可控制事务每个阶段) |
代码侵入性 |
低(无额外代码) |
中(需要编写回调逻辑) |
适用场景 |
90% 的普通业务场景 |
复杂控制、非注解场景 |
五、Spring Boot 事务自动配置:新手也能秒懂的原理
5.1 自动配置流程(图解)
5.2 手动配置 override(进阶)
如果自动配置的事务管理器不满足需求(如多数据源),需手动配置:
@Configuration
public class MultiDataSourceConfig {
@Bean(name = "primaryTransactionManager")
public DataSourceTransactionManager primaryTransactionManager(
@Qualifier("primaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "secondaryTransactionManager")
public DataSourceTransactionManager secondaryTransactionManager(
@Qualifier("secondaryDataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
5.3 事务属性配置(application.properties)
# 全局事务隔离级别(可被方法注解覆盖)
spring.jpa.properties.hibernate.transaction.isolation=READ_COMMITTED
# 配置事务超时时间(单位:秒,对所有事务生效)
spring.transaction.default-timeout=30
六、事务失效?这 8 个坑 90% 的新手都踩过
6.1 坑 1:方法修饰符不是 public
@Service
public class UserService {
// 错误:protected方法不会被AOP代理
@Transactional
protected void updateUser() { ... }
// 错误:private方法无法被代理
@Transactional
private void deleteUser() { ... }
// 正确:public方法
@Transactional
public void saveUser() { ... }
}
6.2 坑 2:同一类内方法调用
@Service
public class OrderService {
// 外部调用有效
@Override
@Transactional
public void createOrder() {
saveOrder(); // 内部调用,事务失效!
}
// 内部方法无注解
private void saveOrder() { ... }
// 正确做法:通过@Autowired注入自身代理
@Autowired
private OrderService selfProxy;
@Override
public void createOrder() {
selfProxy.saveOrder(); // 通过代理调用,事务生效
}
@Transactional
private void saveOrder() { ... }
}
6.3 坑 3:未处理的检查型异常(Checked Exception)
@Transactional
public void updateStock() throws IOException {
// 抛出检查型异常(非RuntimeException)
throw new IOException("库存更新失败");
// 👉 默认不会回滚,因为@Transactional只回滚RuntimeException
}
// 修正:显式指定回滚异常
@Transactional(rollbackFor = IOException.class)
public void updateStock() throws IOException {
throw new IOException("库存更新失败"); // 现在会回滚
}
6.4 坑 4:自动提交未关闭(@Transactional 失效的隐藏原因)
如果使用 MyBatis,需确保SqlSession关闭自动提交:
// MyBatis配置类(重要!)
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
// 关闭自动提交,让Spring事务控制提交
factory.getObject().getConfiguration().setAutoCommit(false);
return factory;
}
6.5 坑 5:事务管理器未正确注入
@Service
public class UserService {
// 错误:未注入事务管理器,直接使用会报空指针
private TransactionTemplate transactionTemplate;
// 正确:通过@Autowired注入
@Autowired
private TransactionTemplate transactionTemplate;
}
6.6 坑 6:使用了错误的事务管理器(多数据源场景)
// 错误:未指定事务管理器,默认使用第一个创建的
@Transactional
public void updatePrimaryDb() { ... }
// 正确:指定事务管理器Bean名称
@Transactional("primaryTransactionManager")
public void updatePrimaryDb() { ... }
6.7 坑 7:异常被吞掉(最隐蔽的坑)
@Transactional
public void processOrder() {
try {
// 可能抛出异常的代码
orderRepository.save(order);
// 故意不处理异常
} catch (Exception e) {
// 仅打印日志,未重新抛出异常
log.error("处理订单失败", e);
}
// 👉 事务不会回滚,因为异常没有传播出去
}
// 修正:重新抛出异常或手动设置回滚
@Transactional
public void processOrder() {
try {
// ...
} catch (Exception e) {
log.error("处理订单失败", e);
// 手动标记回滚(即使不抛异常)
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
6.8 坑 8:类未被 Spring 管理
// 错误:直接new对象,不受Spring事务管理
public class ManualService {
@Autowired
private UserRepository userRepository;
// 事务无效,因为该类未被@Service标记
public void manualTransaction() {
userRepository.save(new User());
}
}
// 正确:添加@Service注解
@Service
public class ManualService {
// 事务生效
}
七、与 MyBatis/JPA 整合:具体场景的最佳实践
7.1 MyBatis 场景:批量操作的事务优化
@Service
public class BatchService {
@Autowired
private UserMapper userMapper;
// 错误:每次插入都开启新事务(性能差)
public void batchInsertOld(List<User> users) {
for (User user : users) {
@Transactional // 注解在循环内,无效!
userMapper.insert(user);
}
}
// 正确:批量操作放在一个事务内
@Transactional
public void batchInsertNew(List<User> users) {
userMapper.batchInsert(users); // 一次性插入(需Mapper支持批量操作)
}
}
7.2 JPA 场景:只读事务优化查询性能
@Service
public class QueryService {
@Autowired
private UserRepository userRepository;
// 标记为只读事务,数据库可优化锁机制
@Transactional(readOnly = true)
public List<User> getUsersByAge(int age) {
return userRepository.findByAge(age);
}
}
7.3 多数据源场景:指定事务管理器
@Service
public class MultiDbService {
// 操作主库事务
@Transactional("primaryTransactionManager")
public void updatePrimaryDb() {
primaryRepository.update(...);
}
// 操作从库事务
@Transactional("secondaryTransactionManager")
public void updateSecondaryDb() {
secondaryRepository.update(...);
}
}
八、分布式事务
8.1 什么是分布式事务?
当操作涉及多个数据库实例(如微服务架构中的订单库和库存库),传统本地事务失效,需要分布式事务解决方案。
8.2 三种主流方案对比
方案 |
核心原理 |
一致性 |
性能 |
适用场景 |
XA 协议 |
两阶段提交(2PC) |
强一致 |
低 |
金融等高一致性场景 |
TCC 模式 |
Try-Confirm-Cancel 三阶段 |
最终一致 |
中 |
分布式电商、支付系统 |
Saga 模式 |
补偿事务(正向操作 + 反向补偿) |
最终一致 |
高 |
长事务、柔性事务场景 |
8.3 Spring 集成 Seata(最简示例)
- 添加依赖:
<dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>1.7.0</version> </dependency>
- 服务层添加全局事务注解:
@GlobalTransactional // 分布式事务注解 public void createOrder(Order order) { orderService.create(order); // 订单库操作 inventoryService.decreaseStock(); // 库存库操作 }
九、事务调试与监控:必备的排查技巧
9.1 开启调试日志(关键!)
在application.properties中添加:
# 开启Spring事务调试日志
logging.level.org.springframework.transaction=DEBUG
# 查看具体的SQL执行日志(MyBatis场景)
logging.level.com.example.mapper=DEBUG
9.2 常用排查命令
- 查看当前事务状态:
TransactionStatus status = TransactionAspectSupport.currentTransactionStatus(); boolean isNewTransaction = status.isNewTransaction(); // 是否新事务 boolean isRollbackOnly = status.isRollbackOnly(); // 是否已标记回滚
- 查看事务管理器配置:
@Autowired private PlatformTransactionManager transactionManager; // 输出事务管理器类型 System.out.println(transactionManager.getClass().getName());
9.3 生产环境监控指标
建议监控以下指标(通过 Micrometer 或 Prometheus):
- spring.transaction.active:当前活动事务数
- spring.transaction.completed:已完成事务数(成功 + 失败)
- transaction.rollback.rate:事务回滚率
十、总结
- 能用声明式就不用编程式:注解优先,减少代码复杂度
- 事务范围尽可能小:
- 错误:在事务中包含网络调用、文件操作
- 正确:仅包裹必要的数据库操作
- 明确指定回滚异常:
@Transactional(rollbackFor = Exception.class) // 回滚所有异常
- 只读事务标记readOnly=true:提升数据库查询性能
- 设置合理的超时时间:避免长事务占用数据库连接
- 多数据源场景指定事务管理器:通过@Transactional("xxxTransactionManager")
- 内部方法调用使用代理对象:通过@Autowired注入自身解决事务失效
- 检查方法修饰符:必须是public方法才能被 AOP 代理
- 关闭 MyBatis 自动提交:确保 Spring 完全控制事务边界
- 开启调试日志:遇到事务问题先看日志,再断点调试
只要按照这个指南一步步实践,即使是完全不懂事务的新手,也能在 Spring 开发中熟练运用事务,确保数据的一致性和完整性。记住:事务的核心是 "要么全做,要么全不做",所有的配置和代码都是为了实现这个目标。遇到问题不要慌,先看日志、再查注解参数、最后断点调试,99% 的问题都能解决!