使用Mybatis批量插入大量数据的实践

news2024/11/27 6:16:20

前言

在项目开发过程中,我们经常会有批量插入的需求,例如:定时统计任务

但是受限于MySQL中 max_allowed_packet 参数限制,5.7版本默认值为4M,这显然不太符合我们的需求,当然我们也可以通过修改此值来适应我们业务,今天分享在不修改此值的情况下,如何在客户端优雅的处理此场景

常用基础版

@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void submit(List<GroupDayStatistics> dataList) {
    // 清理统计数据
    mapper.delete();

    int batchNum = 1000;
    List<Object> tempList = new ArrayList<>(batchNum);
    for (int i = 0, size = dataList.size(); i < size; i++) {
        tempList.add(dataList.get(i));
        if (tempList.size() == 1000 || i + 1 == size) {
            mapper.batchInsert(tempList);
            tempList.clear();
        }
    }
}

这种写法有个缺点:其他地方要用都得copy一份过去,麻烦

改进版

@Service
public class BatchInsertUtil<T> {
    /**
     * 批次新增(删除函数 以及 分片新增都将在同一个事务中进行处理)
     *
     * @param batchSize
     *            批次新增大小
     * @param deleteSupplier
     *            清理函数
     * @param insertFunction
     *            批次新增调用函数
     * @param data
     *            需新增数据
     * @return left:删除影响行 right:新增行数
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public ImmutablePair<Integer, Integer> batchInsert(int batchSize, IntSupplier deleteSupplier,
        Function<List<T>, Integer> insertFunction, List<T> data) {
        int deleteRow = 0;
        // 清理数据
        if (Objects.nonNull(deleteSupplier)) {
            deleteRow = deleteSupplier.getAsInt();
        }
        // 分片分事务插入数据
        List<List<T>> partition = ListUtils.partition(data, batchSize);
        int insertRow = partition.stream().mapToInt(insertFunction::apply).sum();
        return new ImmutablePair<>(deleteRow, insertRow);
    }
}
使用第一步
// 注入我们的工具类
@Resource
private BatchInsertUtil<需要插入的实体类> batchInsertUtil;
使用第二步
IntSupplier deleteSupplier = () -> mapper.delete();
Function<List<需要插入的实体类>, Integer> insertFunction = arg -> mapper.batchInsert(arg);
batchInsertUtil.batchInsert(1000, deleteSupplier, insertFunction, dataList);

本来到这就结束了,问题很多的小明就说了:

你这个怎么删除与分片新增都在一个事务中,如果我一次性插入的数据过多,这不就是一个大事务了嘛?虽然在一个事务中可以保证原子性,但是我有的场景就是想要他们分别处于不同事务,业务上的一致性我自己保证,你就说能不能做吧!

......

......

......

安排!

进一步优化 

// 这里我们在第二步的基础上引入 编程式事务:
@Resource
private PlatformTransactionManager transactionManager;

/**
 * 批次新增(删除函数 以及 分片新增函数都将采取独立事务commit)
 *
 * @param batchSize
 *            批次新增大小
 * @param deleteSupplier
 *            清理函数
 * @param insertFunction
 *            批次新增调用函数
 * @param data
 *            需新增数据
 * @return left:删除影响行 right:新增行数
 */
@Transactional(rollbackFor = Exception.class, propagation = Propagation.NOT_SUPPORTED)
public ImmutablePair<Integer, Integer> batchInsertAloneTransaction(int batchSize, IntSupplier deleteSupplier,
    Function<List<T>, Integer> insertFunction, List<T> data) {

    TransactionStatus transactionStatus = buildTransactionStatus();
    int deleteRow = 0;
    try {
        // 清理数据
        if (Objects.nonNull(deleteSupplier)) {
            deleteRow = deleteSupplier.getAsInt();
        }
        transactionManager.commit(transactionStatus);
    } catch (Exception e) {
        transactionManager.rollback(transactionStatus);
    }
    // 分片分事务插入数据
    List<List<T>> partition = ListUtils.partition(data, batchSize);
    int insertRow = partition.stream().mapToInt(list -> insertAndCommit(insertFunction, list)).sum();
    return new ImmutablePair<>(deleteRow, insertRow);
}

/**
 * 新增分片数据并提交事务
 * 
 * @param insertFunction
 *            新增函数
 * @param dataList
 *            分片数据
 * @return 新增行数
 */
private int insertAndCommit(Function<List<T>, Integer> insertFunction, List<T> dataList) {
    TransactionStatus transactionStatus = buildTransactionStatus();
    try {
        Integer apply = insertFunction.apply(dataList);
        transactionManager.commit(transactionStatus);
        return apply;
    } catch (Exception e) {
        transactionManager.rollback(transactionStatus);
        throw e;
    }
}

/**
 * 构建事务状态
 * 
 * @return 结果
 */
private TransactionStatus buildTransactionStatus() {
    DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
    defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
    return transactionManager.getTransaction(defaultTransactionDefinition);
}

// 注:分片新增任意一个事务操作失败将不对已提交事务产生任何影响,需自行保证数据在业务上的一致性

结语

现在整个世界都变优雅了!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1897438.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ChatGPT如何提升论文写作(附指令集合)

先讲前提&#xff1a; ChatGPT无论是3.5还是4.0都存在非常严重的幻觉问题&#xff0c;目前ChatGPT无法替代搜索引擎。 如果你希望得到更加优质的体验&#xff0c;请用GPT-4.0&#xff0c;幻觉问题上比3.5大幅降低 ChatGPT中文版&#xff0c;一站式AI创作平台​aibox365.com …

昇思MindSpore学习笔记4-01生成式--CycleGAN图像风格迁移互换

摘要&#xff1a; 记录了昇思MindSpore AI框架用循环对抗生成网络模型CycleGAN实现图像匹配的方法、步骤。包括环境准备、数据集下载、数据加载和预处理、构建生成器和判别器、优化、模型训练和推理等。 1.模型介绍 1.1模型简介 CycleGAN(Cycle Generative Adversarial Netwo…

Yolo系列——动态卷积

一、为什么要提出动态卷积&#xff1f; 为了更好的将模型部署在边端设备上&#xff0c;需要设计轻量级网络模型。轻量级卷积网络因其较低的运算而限制了CNN的深度&#xff08;卷积层层数&#xff09;和宽度&#xff08;通道数&#xff09;&#xff0c;限制了模型的表达能力&am…

3dmax全景图用什么渲染软件好?渲染100邀请码1a12

全景图是常见的效果图类型&#xff0c;常用于展示大型空间&#xff0c;如展厅、会议室等。全景图的制作需要渲染&#xff0c;下面我介绍几个常用的渲染软件分享给大家。 1、V-Ray&#xff1a;十分流行的渲染引擎&#xff0c;功能强大&#xff0c;它提供了高质量的光线追踪技术…

gitee代码初次上传步骤

ps. 前提是已经下载安装gitee 一、在本地项目目录下空白处右击&#xff0c;选择“Git Bash Here” 二、初始化 git init 三、添加、提交代码&#xff08;注意add与点之间的空格&#xff09; git add . git commit -m 添加注释 四、连接、推送到gitee仓库 git remote add …

计算机网络——数据链路层(以太网)

目录 局域网的数据链路层 局域网可按照网络拓扑分类 局域网与共享信道 以太网的两个主要标准 适配器与mac地址 适配器的组成与运作 MAC地址 MAC地址的详细介绍 局域网的mac地址格式 mac地址的发送顺序 单播、多播&#xff0c;广播mac地址 mac帧 如何取用…

C++基石:掌握高效编程的艺术

C 关于命名空间&#xff1a;namespace 上述文档详细介绍了C标准库&#xff08;Standard C Library&#xff09;的一些关键约定&#xff0c;这些约定不仅帮助开发者理解如何正确使用库中的功能&#xff0c;也明确了实现者在设计库时的灵活性和限制。下面是对文档中提到的几个要点…

2024-07-05 base SAS programming学习笔记9(variables)

1.在数据集增加累加变量值&#xff08;SUM&#xff09; 求和语句(SUM STATEMENT)&#xff1a;variableexpression variable是累积求和的变量名&#xff0c;为数值型&#xff0c;默认初始值为0&#xff1b;该variable值则会保留到一个观测 当expression有缺失值&#xff0c;在求…

深度学习Week19——学习残差网络和ResNet50V2算法

文章目录 深度学习Week18——学习残差网络和ResNet50V2算法 一、前言 二、我的环境 三、论文解读 3.1 预激活设计 3.2 残差单元结构 四、模型复现 4.1 Residual Block 4.2 堆叠Residual Block 4.3. ResNet50V2架构复现 一、前言 &#x1f368; 本文为&#x1f517;365天深度学…

v-html 空格/换行不生效

接口返回的内容如下&#xff1a;有空格有换行&#xff0c;但 使用v-html无效 需加css样式 white-space: pre-wrap; <div class"pretty-html" v-html"Value"></div>.pretty-html {white-space: pre-wrap; /* 保留空格和换行&#xff0c;并允许…

OZON怎么查看竞品数据,OZON怎么找竞品数据

在跨境电商的激烈竞争中&#xff0c;了解和分析竞品数据是每一位卖家优化销售策略、提升市场竞争力的关键步骤。OZON作为俄罗斯领先的电商平台&#xff0c;为卖家提供了丰富的数据分析工具&#xff0c;而萌啦ozon数据作为第三方数据分析平台&#xff0c;更是为卖家提供了更为全…

Linux 进程与计划任务管理

一、程序、进程、线程的概念 1. 程序&#xff1a;是指一组指示计算机或其他具有信息处理能力装置执行动作或做出判断的指令&#xff0c;通常用某种程序设计语言编写&#xff0c;运行于某种目标计算机体系结构上 2. 进程&#xff1a;是计算机中的软件程序关于某数据集合上的一次…

某yi逆向sign值

声明 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;不提供完整代码&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 本文章未经…

pdf可以删除其中一页吗?6个软件教你快速进行pdf编辑

pdf可以删除其中一页吗&#xff1f;6个软件教你快速进行pdf编辑 编辑PDF文件并删除特定页面是处理文档时常见的需求&#xff0c;特别是在需要定制或精简文件内容时。以下是几款广受欢迎的PDF编辑软件&#xff0c;它们提供了强大的页面删除功能&#xff0c;帮助用户轻松管理和修…

重新定义 AI 部署效率与性能!ZOLOZ 如何借助 AC2 实现低成本高性能的提优?| AI 案例推荐

AI 容器镜像——面向云上全容器场景 王坚院士提到&#xff0c;算力是人工智能发展的三要素之一&#xff0c;而云计算是最好的提供算力的技术手段&#xff0c;英伟达的黄仁勋也表达过类似的观点。可以说&#xff0c;大模型时代下的 AI&#xff0c;天然就是在云场景里的。 容器…

SpringBoot-第一天学习

SpringBoot介绍-约定大于配置 SpringBoot是在Spring4.0基础上开发的&#xff0c;不是替代Spring的解决方案&#xff0c;而是和Spring框架结合并进一步简化Spring搭建和开发过程的。 如何简化&#xff1f;就是通过提供默认配置等方式让我们更容易&#xff0c;集成了大量常用的…

景区智能厕所系统,打造智能化,人性化公共空间

在智慧旅游的大潮中&#xff0c;景区智能厕所系统正逐渐成为提升公共空间智能化、人性化水平的关键载体。作为智慧城市建设的重要组成部分&#xff0c;智能厕所系统不仅解决了传统公厕存在的诸多问题&#xff0c;更通过科技的力量&#xff0c;为游客创造了更加舒适、便捷的如厕…

StreamSets: 数据采集工具详解

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 欢迎关注微信公众号&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&a…

c/c++ 程序运行的过程分析

c/c编译基础知识 GNU GNU&#xff08;GNU’s Not Unix!&#xff09;是一个由理查德斯托曼&#xff08;Richard Stallman&#xff09;在1983年发起的自由软件项目&#xff0c;旨在创建一个完全自由的操作系统&#xff0c;包括操作系统的内核、编译器、工具、库、文本编辑器、邮…

渲染农场渲染真的很贵吗?如何正确使用云渲染农场?

作为渲染行业主流的技术服务“渲染农场"&#xff0c;一直都是备受大家关注&#xff0c;渲染农场最核心的在于充足的计算机算力&#xff0c;结合3D软件支持多台机器渲染的特点&#xff0c;租用渲染农场的机器帮助你快速的解决你的渲染项目。 虽然说渲染农场需要支付一定的…