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

Spring之事务使用指南

Spring之事务使用指南

    • 一、事务的基础概念
      • 1.1 什么是事务?
      • 1.2 事务的ACID特性
      • 1.3 Spring事务的核心优势
    • 二、Spring事务的核心配置
    • 三、事务传播行为(Propagation)
      • 3.1 常用传播行为详解
        • 3.1.1 `REQUIRED`(默认值)
        • 3.1.2 `SUPPORTS`
        • 3.1.3 `REQUIRES_NEW`
        • 3.1.4 `NEVER`
        • 3.1.5 `MANDATORY`
      • 3.2 传播行为选择原则
    • 四、事务隔离级别(Isolation)
      • 4.1 并发事务的三大问题
      • 4.2 隔离级别详解
      • 4.3 配置隔离级别
    • 五、声明式事务实战
      • 5.1 环境准备
      • 5.2 业务实现
        • 5.2.1 Mapper接口(MyBatis)
        • 5.2.2 Service实现
      • 5.3 测试与验证
        • 5.3.1 正常流程(无异常)
        • 5.3.2 异常流程(触发回滚)
    • 六、常见问题与避坑指南
      • 6.1 事务不回滚(@Transactional失效)
        • 6.1.1 异常类型不匹配
        • 6.1.2 方法非public
        • 6.1.3 自调用导致事务失效
      • 6.2 事务超时(Timeout)
      • 6.3 只读事务(readOnly)

事务是保证数据一致性的核心机制,尤其在多步操作的业务场景(如订单创建同时扣减库存)中不可或缺,Spring通过AOP实现了声明式事务管理,简化了传统JDBC手动控制事务的繁琐流程。

一、事务的基础概念

1.1 什么是事务?

事务(Transaction)是由一系列数据库操作组成的逻辑单元,这些操作要么全部成功,要么全部失败(如“创建订单”需同时执行“插入订单记录”和“扣减库存”,两者必须同时成功或同时失败)。

1.2 事务的ACID特性

事务必须满足ACID特性,这是保证数据一致性的基础:

  • 原子性(Atomicity):事务中的操作要么全执行,要么全不执行(如转账时“扣款”和“入账”必须同时成功);
  • 一致性(Consistency):事务执行前后,数据从一个有效状态变为另一个有效状态(如转账前后总金额不变);
  • 隔离性(Isolation):多个事务并发执行时,彼此不干扰(避免脏读、不可重复读等问题);
  • 持久性(Durability):事务提交后,修改永久保存到数据库(即使断电也不丢失)。

1.3 Spring事务的核心优势

传统JDBC事务需要手动控制(conn.setAutoCommit(false)commit()rollback()),而Spring事务的优势在于:

  • 声明式事务:通过注解(@Transactional)或XML配置事务,无需编写事务控制代码;
  • AOP实现:事务逻辑与业务逻辑分离,业务代码只关注核心逻辑;
  • 灵活配置:支持自定义传播行为、隔离级别、超时时间等;
  • 整合方便:与Spring容器无缝集成,支持各种数据源和ORM框架(如MyBatis、Hibernate)。

二、Spring事务的核心配置

Spring事务的核心是@Transactional注解,通过属性配置事务行为,常用属性如下:

属性名作用默认值
propagation事务传播行为(如何处理嵌套事务)Propagation.REQUIRED
isolation事务隔离级别(并发控制)Isolation.DEFAULT(数据库默认)
readOnly是否为只读事务(优化性能)false
timeout事务超时时间(秒,超时自动回滚)-1(无超时)
rollbackFor需要回滚的异常类型(如Exception.classRuntimeException及其子类
noRollbackFor不需要回滚的异常类型

三、事务传播行为(Propagation)

事务传播行为定义了“当一个事务方法调用另一个事务方法时,事务如何传播”,是Spring事务最核心的特性之一。

3.1 常用传播行为详解

3.1.1 REQUIRED(默认值)
  • 规则:如果当前存在事务,则加入该事务;如果没有事务,则创建新事务。
  • 适用场景:大多数业务方法(如订单创建、用户注册)。
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate StockService stockService;// 传播行为:REQUIRED(默认)@Transactional(propagation = Propagation.REQUIRED)public void createOrder(Order order) {orderMapper.insert(order); // 操作1stockService.reduceStock(order.getProductId()); // 调用另一个事务方法}
}@Service
public class StockService {// 传播行为:REQUIRED@Transactional(propagation = Propagation.REQUIRED)public void reduceStock(Long productId) {// 操作2:扣减库存}
}

执行逻辑
createOrder创建事务→reduceStock加入该事务→若操作1或2失败,整个事务回滚(符合原子性)。

3.1.2 SUPPORTS
  • 规则:如果当前存在事务,则加入该事务;如果没有事务,则以非事务方式执行。
  • 适用场景:可选事务的方法(如查询操作,可在事务中执行,也可单独执行)。
@Service
public class UserService {// 传播行为:SUPPORTS@Transactional(propagation = Propagation.SUPPORTS)public User getUserById(Long id) {// 查询用户(非核心操作,有无事务均可)}
}
3.1.3 REQUIRES_NEW
  • 规则:无论当前是否存在事务,都创建新事务(原事务暂停,新事务独立执行)。
  • 适用场景:需要独立事务的操作(如日志记录,即使主事务失败也需提交)。
@Service
public class OrderService {@Autowiredprivate LogService logService;@Transactionalpublic void createOrder(Order order) {// 主事务操作:创建订单logService.recordLog("创建订单:" + order.getId()); // 调用独立事务方法}
}@Service
public class LogService {// 传播行为:REQUIRES_NEW(独立事务)@Transactional(propagation = Propagation.REQUIRES_NEW)public void recordLog(String content) {// 记录日志(即使主事务回滚,此操作仍会提交)}
}

执行逻辑
主事务执行→recordLog创建新事务→日志记录成功提交→主事务若失败,仅主事务回滚,日志不会回滚

3.1.4 NEVER
  • 规则:如果当前存在事务,则抛出异常;如果没有事务,则以非事务方式执行。
  • 适用场景:绝对不能在事务中执行的方法(如某些特殊查询)。
3.1.5 MANDATORY
  • 规则:如果当前存在事务,则加入该事务;如果没有事务,则抛出异常。
  • 适用场景:必须在事务中执行的方法(如核心业务操作)。

3.2 传播行为选择原则

  • 核心业务方法(如订单、支付):REQUIRED
  • 查询方法:SUPPORTS
  • 独立日志、审计操作:REQUIRES_NEW
  • 必须在事务中执行的方法:MANDATORY

四、事务隔离级别(Isolation)

事务隔离级别定义了“多个事务并发执行时的隔离程度”,用于解决并发问题(脏读、不可重复读、幻读)。

4.1 并发事务的三大问题

问题说明示例
脏读读取到另一个未提交事务的修改事务A修改数据→事务B读取→事务A回滚→事务B读取到无效数据
不可重复读同一事务中多次读取数据不一致事务A读取数据→事务B修改并提交→事务A再次读取,数据不同
幻读同一事务中多次查询,结果集数量不一致事务A查询所有订单→事务B新增订单并提交→事务A再次查询,多了一条记录

4.2 隔离级别详解

Spring支持5种隔离级别,对应数据库的隔离级别:

隔离级别解决问题并发性能适用场景
DEFAULT(默认)数据库默认隔离级别(如MySQL默认REPEATABLE_READ中等大多数场景(推荐)
READ_UNCOMMITTED无(允许脏读、不可重复读、幻读)最高极少使用(对一致性要求极低)
READ_COMMITTED解决脏读较高对一致性有基本要求(如Oracle默认)
REPEATABLE_READ解决脏读、不可重复读中等MySQL默认,大多数业务场景
SERIALIZABLE解决所有问题(串行执行)最低对一致性要求极高(如金融交易)

4.3 配置隔离级别

@Service
public class OrderService {// 设置隔离级别为REPEATABLE_READ@Transactional(isolation = Isolation.REPEATABLE_READ)public void createOrder(Order order) {// 业务逻辑}
}

注意:隔离级别受数据库支持限制(如MySQL支持所有级别,SQL Server不支持READ_UNCOMMITTED),实际以数据库为准。

五、声明式事务实战

以“订单创建”为例,演示事务的完整使用(包含传播行为、异常回滚配置)。

5.1 环境准备

添加Spring事务依赖(已包含在spring-context中),并配置数据源和事务管理器:

@Configuration
@MapperScan("com.example.mapper")
public class SpringConfig {// 数据源配置(省略,需配置正确的JDBC连接)@Beanpublic DataSource dataSource() { ... }// 事务管理器(核心)@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {return new DataSourceTransactionManager(dataSource);}
}

5.2 业务实现

5.2.1 Mapper接口(MyBatis)
public interface OrderMapper {void insert(Order order);
}public interface StockMapper {void reduceStock(@Param("productId") Long productId, @Param("quantity") Integer quantity);
}
5.2.2 Service实现
@Service
public class OrderService {@Autowiredprivate OrderMapper orderMapper;@Autowiredprivate StockService stockService;/*** 创建订单(核心业务)* 传播行为:REQUIRED(默认)* 回滚规则:所有Exception都回滚(默认仅RuntimeException回滚,此处扩展)*/@Transactional(rollbackFor = Exception.class)public void createOrder(Order order) throws Exception {// 1. 创建订单orderMapper.insert(order);// 2. 扣减库存(调用另一个事务方法)stockService.reduceStock(order.getProductId(), order.getQuantity());// 3. 模拟异常(测试回滚)if (order.getTotalAmount() < 0) {throw new Exception("订单金额不能为负"); // 触发回滚}}
}@Service
public class StockService {@Autowiredprivate StockMapper stockMapper;// 传播行为:REQUIRED(加入订单事务)@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)public void reduceStock(Long productId, Integer quantity) {stockMapper.reduceStock(productId, quantity);// 若库存不足,抛出异常(触发回滚)if (/* 库存不足 */) {throw new RuntimeException("库存不足");}}
}

5.3 测试与验证

5.3.1 正常流程(无异常)
@Test
public void testCreateOrderSuccess() {Order order = new Order();order.setProductId(1L);order.setQuantity(2);order.setTotalAmount(100.0);orderService.createOrder(order);// 验证:订单表新增记录,库存表数量减少(事务提交成功)
}
5.3.2 异常流程(触发回滚)
@Test
public void testCreateOrderRollback() {Order order = new Order();order.setProductId(1L);order.setQuantity(2);order.setTotalAmount(-100.0); // 触发异常try {orderService.createOrder(order);} catch (Exception e) {// 验证:订单表无新增记录,库存表数量未变(事务回滚成功)}
}

六、常见问题与避坑指南

6.1 事务不回滚(@Transactional失效)

6.1.1 异常类型不匹配

问题:方法抛出CheckedException(如Exception),但rollbackFor未配置,导致事务不回滚。

原因@Transactional默认只对RuntimeException及其子类回滚,对CheckedException不回滚。

解决方案
通过rollbackFor指定需要回滚的异常:

// 对所有Exception回滚
@Transactional(rollbackFor = Exception.class)
6.1.2 方法非public

问题:非public方法(如privateprotected)的@Transactional无效。

原因:Spring AOP默认只对public方法增强(事务基于AOP实现)。

解决方案
确保事务方法为public

6.1.3 自调用导致事务失效

问题:同一类中方法调用(自调用)时,事务不生效。

@Service
public class OrderService {public void methodA() {methodB(); // 自调用,事务不生效}@Transactionalpublic void methodB() { ... }
}

原因:Spring事务基于代理对象,自调用是目标对象内部调用,未经过代理。

解决方案

  1. 注入自身代理对象调用:
@Service
public class OrderService {@Autowiredprivate OrderService orderService; // 注入自身代理public void methodA() {orderService.methodB(); // 通过代理调用,事务生效}@Transactionalpublic void methodB() { ... }
}
  1. 开启暴露代理(@EnableAspectJAutoProxy(exposeProxy = true)),通过AopContext获取代理:
public void methodA() {((OrderService) AopContext.currentProxy()).methodB();
}

6.2 事务超时(Timeout)

问题:事务执行时间过长,占用数据库连接,导致连接池耗尽。

解决方案
设置合理的超时时间(秒),超时自动回滚并释放连接:

// 超时时间30秒
@Transactional(timeout = 30)
public void createOrder(Order order) { ... }

6.3 只读事务(readOnly)

对查询方法设置readOnly = true,提示数据库优化事务(如避免写操作、启用缓存):

// 只读事务(查询方法)
@Transactional(readOnly = true)
public List<Order> getOrders() { ... }

注意:只读事务中执行insert/update会抛出异常(取决于数据库)。

总结:事务使用的最佳实践
Spring事务简化了事务管理,但需遵循最佳实践避免常见问题:

  1. 核心配置
  • 对所有业务方法显式指定rollbackFor = Exception.class(避免异常不回滚);
  • 核心业务用REQUIRED传播行为,独立操作(如日志)用REQUIRES_NEW
  • 查询方法设置readOnly = true优化性能。
  1. 避坑要点
  • 事务方法必须为public
  • 避免自调用(或通过代理对象调用);
  • 合理设置超时时间,避免长事务。
  1. 性能优化
  • 事务范围尽可能小(仅包含必要操作,避免在事务中调用外部接口、等待用户输入);
  • 读多写少场景用较高隔离级别(如READ_COMMITTED),减少锁竞争。

事务是保证数据一致性的最后一道防线,正确使用能避免数据错乱(如重复下单、库存超卖)等严重问题。建议在开发中结合日志(如打印事务开始/提交/回滚日志)和测试(模拟异常验证回滚)确保事务生效。

若这篇内容帮到你,动动手指支持下!关注不迷路,干货持续输出!
ヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノヾ(´∀ ˋ)ノ

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

相关文章:

  • Java行为型模式---解释器模式
  • Openlayers 面试题及答案180道(121-140)
  • Node.js Express keep-alive 超时时间设置
  • @import导入css样式、scss变量用法、static目录
  • Java中List<int[]>()和List<int[]>[]的区别
  • PAT 1049 Counting Ones
  • 医学图像超分辨率重建深度学习模型开发报告
  • 如何用immich将苹果手机中的照片备份到指定文件夹
  • Word for mac使用宏
  • UniApp 常用UI库
  • 机器视觉---深度图像存储格式
  • 闲庭信步使用图像验证平台加速FPGA的开发:第二十五课——正弦波图像的FPGA实现
  • 数据存储方案h5py
  • 【C++基础】面试高频考点解析:extern “C“ 的链接陷阱与真题实战
  • MySQL详解三
  • MyBatis Plus高效开发指南
  • 第459场周赛
  • ESXi6.7硬件传感器红色警示信息
  • 详解Mysql解决深分页方案
  • Python类中方法种类与修饰符详解:从基础到实战
  • [simdjson] ondemand::value | object array
  • 低速信号设计之I3C篇
  • 嵌入式Linux:获取线程ID
  • gym 安装
  • PrimeTime:高级片上变化(AOCV)
  • Laravel 框架NOAUTH Authentication required 错误解决方案-优雅草卓伊凡
  • 分享如何在保证画质的前提下缩小视频体积实用方案
  • NISP-PTE基础实操——XSS
  • MybatisPlus-14.扩展功能-DB静态工具-练习
  • windows + phpstorm 2024 + phpstudy 8 + php7.3 + thinkphp6 配置xdebug调试