【Java开发】Spring 事务开发完全指南:从入门到精通
目录
一、为什么需要事务?从一个真实案例说起
二、Spring 事务核心概念:先搞懂这三个关键组件
2.1 三大核心接口
三、声明式事务:99% 的场景都用它(附完整代码示例)
3.1 最简入门:3 步搞定基础使用
步骤 1:添加依赖(Maven/Gradle)
步骤 2:配置事务管理器(Spring Boot 自动配置,非 Boot 需手动)
步骤 3:在 Service 方法上添加 @Transactional 注解
3.2 @Transactional 注解全参数解析
传播行为深度解析(最容易混淆的点)
四、编程式事务:适合复杂场景的精细控制
4.1 什么时候用编程式事务?
4.2 代码示例:使用 TransactionTemplate
4.3 对比声明式 vs 编程式
五、Spring Boot 事务自动配置:新手也能秒懂的原理
5.1 自动配置流程(图解)
5.2 手动配置 override(进阶)
5.3 事务属性配置(application.properties)
六、事务失效?这 8 个坑 90% 的新手都踩过
6.1 坑 1:方法修饰符不是 public
6.2 坑 2:同一类内方法调用
6.3 坑 3:未处理的检查型异常(Checked Exception)
6.4 坑 4:自动提交未关闭(@Transactional 失效的隐藏原因)
6.5 坑 5:事务管理器未正确注入
6.6 坑 6:使用了错误的事务管理器(多数据源场景)
6.7 坑 7:异常被吞掉(最隐蔽的坑)
6.8 坑 8:类未被 Spring 管理
七、与 MyBatis/JPA 整合:具体场景的最佳实践
7.1 MyBatis 场景:批量操作的事务优化
7.2 JPA 场景:只读事务优化查询性能
7.3 多数据源场景:指定事务管理器
八、分布式事务
8.1 什么是分布式事务?
8.2 三种主流方案对比
8.3 Spring 集成 Seata(最简示例)
九、事务调试与监控:必备的排查技巧
9.1 开启调试日志(关键!)
9.2 常用排查命令
9.3 生产环境监控指标
十、总结
一、为什么需要事务?从一个真实案例说起
假设你正在开发一个电商系统,用户下单时需要同时完成两个操作:
- 创建订单记录(插入订单表)
- 扣减库存(更新商品表库存字段)
如果没有事务保护,可能会出现以下情况:
- 订单创建成功,但库存扣减失败
- 系统突然断电,导致部分操作未持久化
- 多用户并发下单时,库存出现负数
事务的核心价值:确保这两个操作要么全部成功,要么全部失败,就像一个 "原子操作"。
二、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 {@Beanpublic DataSourceTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}
步骤 3:在 Service 方法上添加 @Transactional 注解
@Service
public class OrderService {@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate ProductRepository productRepository;// 核心业务方法,添加事务注解@Transactionalpublic 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 {@Autowiredprivate TransactionTemplate transactionTemplate;@Autowiredprivate OrderRepository orderRepository;@Autowiredprivate ProductRepository productRepository;public void createOrderWithProgrammaticTransaction(Order order) {transactionTemplate.execute(status -> { // 传入TransactionCallbacktry {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代理@Transactionalprotected void updateUser() { ... }// 错误:private方法无法被代理@Transactionalprivate void deleteUser() { ... }// 正确:public方法@Transactionalpublic void saveUser() { ... }
}
6.2 坑 2:同一类内方法调用
@Service
public class OrderService {// 外部调用有效@Override@Transactionalpublic void createOrder() {saveOrder(); // 内部调用,事务失效!}// 内部方法无注解private void saveOrder() { ... }// 正确做法:通过@Autowired注入自身代理@Autowiredprivate OrderService selfProxy;@Overridepublic void createOrder() {selfProxy.saveOrder(); // 通过代理调用,事务生效}@Transactionalprivate 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注入@Autowiredprivate 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 {@Autowiredprivate UserRepository userRepository;// 事务无效,因为该类未被@Service标记public void manualTransaction() {userRepository.save(new User());}
}// 正确:添加@Service注解
@Service
public class ManualService {// 事务生效
}
七、与 MyBatis/JPA 整合:具体场景的最佳实践
7.1 MyBatis 场景:批量操作的事务优化
@Service
public class BatchService {@Autowiredprivate UserMapper userMapper;// 错误:每次插入都开启新事务(性能差)public void batchInsertOld(List<User> users) {for (User user : users) {@Transactional // 注解在循环内,无效!userMapper.insert(user);}}// 正确:批量操作放在一个事务内@Transactionalpublic void batchInsertNew(List<User> users) {userMapper.batchInsert(users); // 一次性插入(需Mapper支持批量操作)}
}
7.2 JPA 场景:只读事务优化查询性能
@Service
public class QueryService {@Autowiredprivate 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% 的问题都能解决!