2025-07-26 start:
spring事务的传播行为:
传播行为 | 当前有事务 | 当前无事务 |
---|---|---|
REQUIRED | 加入当前事务 | 新建事务 |
SUPPORTS | 加入当前事务 | 非事务执行 |
MANDATORY | 加入当前事务 | 抛出异常 |
REQUIRES_NEW | 挂起当前事务,新建事务 | 新建事务 |
NOT_SUPPORTED | 挂起当前事务,非事务执行 | 非事务执行 |
NEVER | 抛出异常 | 非事务执行 |
NESTED | 创建嵌套事务 | 新建事务 |
实际应用建议
-
默认选择REQUIRED:大多数业务方法适用
-
查询方法使用SUPPORTS:提高性能
-
需要独立事务使用REQUIRES_NEW:如日志记录
-
需要部分回滚使用NESTED:复杂业务场景
-
强制事务检查使用MANDATORY:确保方法在事务中执行
注意事项
-
不同的传播行为会影响事务的提交和回滚行为
-
传播行为的选择会影响系统性能和一致性
-
嵌套事务(NESTED)需要底层数据库支持保存点(Savepoint)
-
REQUIRES_NEW会创建新连接,可能影响性能
深入理解Spring中的嵌套事务(NESTED)
嵌套事务(NESTED)是Spring事务传播行为中最复杂但也最有用的一种模式,它允许在一个现有事务中创建"子事务",提供了更精细的事务控制能力。
嵌套事务的核心概念
1. 基本定义
嵌套事务是指在一个已经存在的事务内部启动的新事务,它具有以下特点:
-
嵌套事务是外部事务的一部分
-
嵌套事务可以独立回滚而不影响外部事务
-
外部事务回滚会导致所有嵌套事务回滚
2. 与REQUIRES_NEW的区别
特性 | NESTED | REQUIRES_NEW |
---|---|---|
事务关系 | 父子关系 | 完全独立的新事务 |
回滚影响 | 子事务回滚不影响父事务 | 新事务回滚不影响原事务 |
连接使用 | 使用同一个数据库连接 | 使用新的数据库连接 |
性能影响 | 较小 | 较大(需要新连接) |
数据库要求 | 需要支持保存点(Savepoint) | 不需要特殊支持 |
嵌套事务的实现机制
1. 基于保存点(Savepoint)
Spring的嵌套事务实际上是通过数据库的保存点机制实现的:
-
开始嵌套事务时:在当前事务中创建一个保存点
-
嵌套事务回滚时:回滚到该保存点
-
嵌套事务提交时:实际上不会立即提交,而是等待外部事务提交
2. 工作流程
-
外部事务开始
-
遇到嵌套事务方法:
-
设置保存点
-
执行嵌套事务方法
-
-
如果嵌套事务失败:
-
回滚到保存点
-
外部事务可以继续
-
-
如果嵌套事务成功:
-
保持更改
-
继续外部事务
-
-
外部事务提交时,所有嵌套事务的更改才真正提交
实际应用场景
1. 部分操作可失败的业务流程
@Transactional public void orderProcess(Order order) { // 主业务逻辑 orderService.createOrder(order); try { // 嵌套事务:发送积分 pointService.addPoints(order.getUserId(), order.getAmount()); } catch (Exception e) { // 积分操作失败不影响主订单 logger.error("积分处理失败", e); } // 其他业务逻辑 } // 积分服务方法 @Transactional(propagation = Propagation.NESTED) public void addPoints(Long userId, BigDecimal amount) { // 积分处理逻辑 }
2. 批量处理中的部分回滚
@Transactional public void batchImport(List<Data> dataList) { for (Data data : dataList) { try { // 每条记录的处理是嵌套事务 processSingleData(data); } catch (Exception e) { // 单条失败不影响其他记录 logError(data, e); } } } @Transactional(propagation = Propagation.NESTED) public void processSingleData(Data data) { // 单条数据处理逻辑 }
使用注意事项
-
数据库支持:并非所有数据库都支持保存点,使用前需确认
-
支持:Oracle、PostgreSQL、SQL Server等
-
不支持:MySQL的MyISAM引擎
-
-
隔离级别影响:嵌套事务会继承外部事务的隔离级别
-
异常处理:
-
嵌套事务抛出异常默认会标记事务为rollback-only
-
可以通过try-catch捕获异常避免外部事务回滚
-
-
性能考虑:
-
比REQUIRES_NEW性能好(不新建连接)
-
但比REQUIRED略耗资源(需要维护保存点)
-
-
与@Transactional一起使用:
@Transactional(propagation = Propagation.NESTED) public void nestedMethod() { // 方法实现 }
总结
嵌套事务(NESTED)提供了比REQUIRES_NEW更轻量级的部分事务回滚能力,特别适合以下场景:
-
主业务流程必须完成,次要操作可以失败
-
批量处理中需要单条记录失败不影响其他记录
-
需要精细控制事务回滚范围的复杂业务
end
使用@Transactional进行回滚时,当异常被try捕获处理后,Spring提供的事务将不会进行回滚
相关文章如下:
关于Spring事务回滚@Transactional使用记录
@transactional注解在什么情况下会失效,为什么。
[解决] spring service 调用当前类方法事务不生效
2022-05-22-0 15:45
start
这个是在网上看来的一个问题,觉得有点意思,记录一下
上面的的代码咋一看没有什么问题,不过仔细看就能看出一些端倪,
当a线程进入上诉流程中加锁解锁,此时数据库事务尚未提交,这是b线程进来了,这个时候是没有锁的,也就是说b线程读取数据库读取到的是原来的数据
然后提交事务。
在高并发的情况下
这样导致的问题是数据库的商品数量并不能如愿卖多少,减多少。而是会出现超卖的情况,即卖多件商品,只会减一个库存
end