关于Spring事务回滚@Transactional使用记录

我爱海鲸 2025-07-26 16:10:09 暂无标签

简介在使用Spring事务存在的一些疑惑、事务传播行为

2025-07-26 start:

spring事务的传播行为:

传播行为 当前有事务 当前无事务
REQUIRED 加入当前事务 新建事务
SUPPORTS 加入当前事务 非事务执行
MANDATORY 加入当前事务 抛出异常
REQUIRES_NEW 挂起当前事务,新建事务 新建事务
NOT_SUPPORTED 挂起当前事务,非事务执行 非事务执行
NEVER 抛出异常 非事务执行
NESTED 创建嵌套事务 新建事务

实际应用建议

  1. 默认选择REQUIRED:大多数业务方法适用

  2. 查询方法使用SUPPORTS:提高性能

  3. 需要独立事务使用REQUIRES_NEW:如日志记录

  4. 需要部分回滚使用NESTED:复杂业务场景

  5. 强制事务检查使用MANDATORY:确保方法在事务中执行

注意事项

  1. 不同的传播行为会影响事务的提交和回滚行为

  2. 传播行为的选择会影响系统性能和一致性

  3. 嵌套事务(NESTED)需要底层数据库支持保存点(Savepoint)

  4. REQUIRES_NEW会创建新连接,可能影响性能

 

深入理解Spring中的嵌套事务(NESTED)

嵌套事务(NESTED)是Spring事务传播行为中最复杂但也最有用的一种模式,它允许在一个现有事务中创建"子事务",提供了更精细的事务控制能力。

嵌套事务的核心概念

1. 基本定义

嵌套事务是指在一个已经存在的事务内部启动的新事务,它具有以下特点:

  • 嵌套事务是外部事务的一部分

  • 嵌套事务可以独立回滚而不影响外部事务

  • 外部事务回滚会导致所有嵌套事务回滚

2. 与REQUIRES_NEW的区别

特性 NESTED REQUIRES_NEW
事务关系 父子关系 完全独立的新事务
回滚影响 子事务回滚不影响父事务 新事务回滚不影响原事务
连接使用 使用同一个数据库连接 使用新的数据库连接
性能影响 较小 较大(需要新连接)
数据库要求 需要支持保存点(Savepoint) 不需要特殊支持

嵌套事务的实现机制

1. 基于保存点(Savepoint)

Spring的嵌套事务实际上是通过数据库的保存点机制实现的:

  • 开始嵌套事务时:在当前事务中创建一个保存点

  • 嵌套事务回滚时:回滚到该保存点

  • 嵌套事务提交时:实际上不会立即提交,而是等待外部事务提交

2. 工作流程

  1. 外部事务开始

  2. 遇到嵌套事务方法:

    • 设置保存点

    • 执行嵌套事务方法

  3. 如果嵌套事务失败:

    • 回滚到保存点

    • 外部事务可以继续

  4. 如果嵌套事务成功:

    • 保持更改

    • 继续外部事务

  5. 外部事务提交时,所有嵌套事务的更改才真正提交

实际应用场景

1. 部分操作可失败的业务流程

java
 
@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. 批量处理中的部分回滚

java
 
@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) {
    // 单条数据处理逻辑
}

使用注意事项

  1. 数据库支持:并非所有数据库都支持保存点,使用前需确认

    • 支持:Oracle、PostgreSQL、SQL Server等

    • 不支持:MySQL的MyISAM引擎

  2. 隔离级别影响:嵌套事务会继承外部事务的隔离级别

  3. 异常处理

    • 嵌套事务抛出异常默认会标记事务为rollback-only

    • 可以通过try-catch捕获异常避免外部事务回滚

  4. 性能考虑

    • 比REQUIRES_NEW性能好(不新建连接)

    • 但比REQUIRED略耗资源(需要维护保存点)

  5. 与@Transactional一起使用

    java
     
    @Transactional(propagation = Propagation.NESTED)
    public void nestedMethod() {
        // 方法实现
    }

总结

嵌套事务(NESTED)提供了比REQUIRES_NEW更轻量级的部分事务回滚能力,特别适合以下场景:

  • 主业务流程必须完成,次要操作可以失败

  • 批量处理中需要单条记录失败不影响其他记录

  • 需要精细控制事务回滚范围的复杂业务

end

使用@Transactional进行回滚时,当异常被try捕获处理后,Spring提供的事务将不会进行回滚

undefined

相关文章如下:

关于Spring事务回滚@Transactional使用记录

Spring声明式事务管理(基于XML方式实现)

 

@transactional注解在什么情况下会失效,为什么。

 

[解决] spring service 调用当前类方法事务不生效

2022-05-22-0 15:45

start

这个是在网上看来的一个问题,觉得有点意思,记录一下

上面的的代码咋一看没有什么问题,不过仔细看就能看出一些端倪,

当a线程进入上诉流程中加锁解锁,此时数据库事务尚未提交,这是b线程进来了,这个时候是没有锁的,也就是说b线程读取数据库读取到的是原来的数据

然后提交事务。

在高并发的情况下

这样导致的问题是数据库的商品数量并不能如愿卖多少,减多少。而是会出现超卖的情况,即卖多件商品,只会减一个库存

end

 

 

 

 

你好:我的2025