当前位置: 首页 > ops >正文

【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 生产环境监控指标

十、总结


一、为什么需要事务?从一个真实案例说起

假设你正在开发一个电商系统,用户下单时需要同时完成两个操作:

  1. 创建订单记录(插入订单表)
  2. 扣减库存(更新商品表库存字段)

如果没有事务保护,可能会出现以下情况:

  • 订单创建成功,但库存扣减失败
  • 系统突然断电,导致部分操作未持久化
  • 多用户并发下单时,库存出现负数

事务的核心价值:确保这两个操作要么全部成功,要么全部失败,就像一个 "原子操作"。

二、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(最简示例)

  1. 添加依赖:
    <dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.7.0</version>
    </dependency>
  2. 服务层添加全局事务注解:
    @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 常用排查命令

  1. 查看当前事务状态:
    TransactionStatus status = TransactionAspectSupport.currentTransactionStatus();
    boolean isNewTransaction = status.isNewTransaction(); // 是否新事务
    boolean isRollbackOnly = status.isRollbackOnly(); // 是否已标记回滚
  2. 查看事务管理器配置:
    @Autowired
    private PlatformTransactionManager transactionManager;// 输出事务管理器类型
    System.out.println(transactionManager.getClass().getName()); 

9.3 生产环境监控指标

建议监控以下指标(通过 Micrometer 或 Prometheus):

  • spring.transaction.active:当前活动事务数
  • spring.transaction.completed:已完成事务数(成功 + 失败)
  • transaction.rollback.rate:事务回滚率

十、总结

  1. 能用声明式就不用编程式:注解优先,减少代码复杂度
  2. 事务范围尽可能小
    1. 错误:在事务中包含网络调用、文件操作
    2. 正确:仅包裹必要的数据库操作
  3. 明确指定回滚异常
    @Transactional(rollbackFor = Exception.class) // 回滚所有异常

    ​​​​​​​

  4. 只读事务标记readOnly=true:提升数据库查询性能
  5. 设置合理的超时时间:避免长事务占用数据库连接
  6. 多数据源场景指定事务管理器:通过@Transactional("xxxTransactionManager")
  7. 内部方法调用使用代理对象:通过@Autowired注入自身解决事务失效
  8. 检查方法修饰符:必须是public方法才能被 AOP 代理
  9. 关闭 MyBatis 自动提交:确保 Spring 完全控制事务边界
  10. 开启调试日志:遇到事务问题先看日志,再断点调试
​​​​​​​​​​​​​​

只要按照这个指南一步步实践,即使是完全不懂事务的新手,也能在 Spring 开发中熟练运用事务,确保数据的一致性和完整性。记住:事务的核心是 "要么全做,要么全不做",所有的配置和代码都是为了实现这个目标。遇到问题不要慌,先看日志、再查注解参数、最后断点调试,99% 的问题都能解决!

http://www.xdnf.cn/news/13320.html

相关文章:

  • MySQL中触发器详解 触发器在自动化任务中的应用场景
  • 第27节 Node.js Buffer
  • 【编译工具】(自动化)AI 赋能的自动化测试工具:如何让测试效率提升 500% 并实现智能质检?
  • UML用例分析与用例规约表:以聊天室软件为例
  • Odoo 17 在线聊天报错 “Couldn‘t bind the websocket...“ 的解决方案
  • gitHub hexo 个人博客升级版
  • springboot + nacos + k8s 优雅停机
  • Go 通道(Channel)入门与基础使用
  • P2842 纸币问题 1
  • SpringBoot + 自建GitLab + Jenkins + CentOS Stream 9 来实现自动化部署
  • 商品中心—3.商品可采可补可售的技术文档上
  • Mybatis辅助配置-配置SQL提示
  • 2024 CKS题库+详尽解析| 1. kube-bench 修复不安全项
  • 提取 Word 中图片原始质量
  • 浅谈HDFS--基本操作
  • 进程信号之signal系统调用
  • 【编译工具】(自动化)自动化测试工具:如何让我的开发效率提升300%并保证代码质量?
  • UniApp APP打包方法(Android/iOS双平台)
  • SQL进阶之旅 Day 26:分库分表环境中的SQL策略
  • 三数之和-力扣
  • BUUCTF两道目录包含题目
  • 电动阀门领域的后起之秀:舵机,速度与精度并重
  • AI【应用 01】Trae Agent Gitee自动化辅助神器(使用 MCP tools 创建自定义 Trae Agent 的探索分享)
  • 自定义鼠标效果 - 浏览器扩展使用教程
  • Linux驱动:framebuffer应用层实践
  • React Native UI 框架与动画系统:打造专业移动应用界面
  • vue中的v-model指令和组件通信机制
  • MyBatis实战指南(七)MyBatis缓存机制
  • PosterSQL日常维护
  • Asp.Net Core SignalR导入数据