批量新增操作为什么要加@Transactional注解

java 复制代码
@Override
@Transactional
public void batchAdd(List<ServeUpsertReqDTO> serveUpsertReqDTOList) {
    for (ServeUpsertReqDTO serveUpsertReqDTO : serveUpsertReqDTOList) {
        //1.校验服务项是否为启用状态,不是启用状态不能新增
        ServeItem serveItem = serveItemMapper.selectById(serveUpsertReqDTO.getServeItemId());
        //如果服务项信息不存在或未启用
        if (ObjectUtil.isNull(serveItem) || serveItem.getActiveStatus() != FoundationStatusEnum.ENABLE.getStatus()) {
            throw new ForbiddenOperationException("该服务未启用无法添加到区域下使用");
        }

        //2.校验是否重复新增
        Long count = lambdaQuery()
                .eq(Serve::getRegionId, serveUpsertReqDTO.getRegionId())
                .eq(Serve::getServeItemId, serveUpsertReqDTO.getServeItemId())
                .count();
        if (count > 0) {
            throw new ForbiddenOperationException(serveItem.getName() + "服务已存在");
        }

        //3.新增服务
        Serve serve = BeanUtil.toBean(serveUpsertReqDTO, Serve.class);
        //默认为服务项的价格
        serve.setPrice(serveItem.getReferencePrice());
        Region region = regionMapper.selectById(serveUpsertReqDTO.getRegionId());
        serve.setCityCode(region.getCityCode());
        baseMapper.insert(serve);
    }
}

为什么这段 batchAdd 方法需要添加 @Transactional 注解,以及它的具体作用。

核心问题:数据一致性

batchAdd 方法的目的是批量新增 一系列服务。在这个过程中,它执行了多个独立的数据库写操作(baseMapper.insert(serve))。

想象一下,如果在批量处理的过程中发生了以下意外情况:

  • 批量列表中有10条数据。
  • 程序成功插入了前5条。
  • 在插入第6条时,数据库突然断电、或者网络中断、或者代码抛出了异常(比如后面的业务逻辑有问题)。

如果没有 @Transactional 注解,会发生什么?

  • 数据库中会残留前5条成功插入的数据。
  • 第6条及以后的数据没有被插入。
  • 这导致了数据不一致的状态。你的业务数据可能因此变得混乱,例如,部分数据成功导入,部分失败,而系统却没有任何记录或回滚机制。

@Transactional 的作用:保证事务的ACID特性

@Transactional 注解的核心作用就是将一个方法包装在一个数据库事务 中。事务遵循 ACID 原则,对于我们这个场景来说,最重要的是 Atomicity(原子性) 和 Consistency(一致性)。

  • 原子性 (Atomicity) : 事务中的所有操作被视为一个不可分割的整体。要么全部成功 ,要么全部失败。

    • 在 batchAdd 中,要么列表里的所有 Serve 都被成功插入数据库,要么在发生任何错误时,所有 已插入的 Serve 都会被撤销(回滚),数据库回到方法执行前的状态。
  • 一致性 (Consistency): 事务执行前后,数据库的完整性约束没有被破坏。

    • 通过保证原子性,@Transactional 确保了批量新增操作不会导致数据处于部分完成的不一致状态。

这个方法中 @Transactional 的具体行为

当 batchAdd 方法被调用时,Spring 框架会拦截这个调用,并:

  1. 开启一个新的数据库事务。
  2. 执行方法体内部的逻辑:循环、校验、插入。
  3. 如果方法正常执行完毕 :Spring 会自动提交 (Commit) 这个事务,所有在循环中执行的 insert 操作都会被永久保存到数据库中。
  4. 如果方法执行过程中抛出了异常 (例如 throw new ForbiddenOperationException(...)):Spring 会自动回滚 (Rollback) 这个事务。数据库会撤销在本次事务中所有已执行的 insert 操作,仿佛这个方法从未执行过一样。

为什么这个方法特别需要 @Transactional?

  1. 多步写操作 :它包含了一个循环,循环内部会执行多次 insert。这是最典型的需要事务保护的场景。
  2. 存在前置校验:方法在插入前做了"服务项是否启用"和"是否重复"的校验。如果没有事务,在高并发场景下,可能会出现两个线程同时通过校验,然后都执行插入,导致重复数据(虽然你的校验能防止大部分情况,但事务是最后的保障)。更重要的是,如果校验通过后,插入时失败,事务可以保证不会留下脏数据。
  3. 业务逻辑要求:从业务角度看,"批量新增"这个操作本身就应该是一个原子操作。用户期望的是要么全部添加成功,要么全部失败。

总结

@Transactional 注解为 batchAdd 方法提供了事务保障 。它确保了在批量插入一系列服务记录时,整个过程是原子的、一致的。如果其中任何一个环节出了问题,所有已完成的操作都会被撤销,从而避免了部分成功、部分失败所导致的数据不一致问题,让批量操作的结果更加可靠和可预测。

简单来说,它就是为了保证:要么全成,要么全败。

相关推荐
lambo mercy44 分钟前
Day3:结构化输出
java·数据库·oracle
派小心.1 小时前
小程序分享链路中来源参数的传递校验
大数据·前端·数据库·人工智能·小程序·数据分析
Είναι η κοπέλα1 小时前
向量数据库与嵌入模型:Chroma/FAISS/Milvus 选型
数据库·milvus·faiss
IvorySQL1 小时前
从轮询到等待——PostgreSQL WAIT FOR LSN 特性深度解析
数据库·postgresql
OceanBase数据库官方博客2 小时前
被 VLDB 2027接收OceanBase 最新论文让数据库基(技术解析与实践)
数据库·人工智能·oceanbase
创邻科技3 小时前
国产图数据库刷新 LDBC 世界纪录:创邻科技 Galaxybase 在 SF100、SF300、SF1000 测试中全面领先”
数据库·科技
GreatSQL社区3 小时前
主从复制SQL线程中断——字符集砸出的坑
java·数据库·算法
XLYcmy3 小时前
AI 赋能后中文与英文数据库对比分析报告 上
数据库·ai·llm·prompt·检索·wos·cnki
这个DBA有点耶4 小时前
ELT 工具选型完整指南:从 ETL 差异分析到工程落地
数据库·架构·代码规范
IvorySQL4 小时前
PostgreSQL 日报|堆表选择性索引更新(10 月 13 日)
数据库·人工智能·postgresql·开源·区块链