SpringBoot +MyBatis批量插入数据

2023-09-18 18:29:25

💍 背景介绍

在最近的开发过程中,遇到了往数据库中表中插入大量的数据。有一个全国银行各分行的信息,共计148032条数据
文件有8.45MB,因为考虑到数据量比较大,就想着导入到MySQL看一看需要多长时间。

💍 方案一:用 for语句循环插入(不推荐)

使用for循环语句将,将数据一条条插入。

insert into t_bank values (?, ?, ?, ?, ?)
 /**
     * 导入银行信息
     *
     * @param bankList
     * @return java.lang.String
     */
    @Override
    public String importBank(List<TBank> bankList) {
        if (StringUtils.isNull(bankList) || bankList.size() == 0)
        {
            throw new CustomException("导入用户数据不能为空!");
        }
        long start = System.currentTimeMillis();
        for (int i = 0; i < bankList.size(); i++) {
            tBankMapper.insertTBank(bankList.get(i));
        }
        long end = System.currentTimeMillis();
        log.info("数据总耗时:" + (end-start) + "ms" );
        return "Success";
    }

优势:JDBC 中的 PreparedStatement 有预编译功能,预编译之后会缓存起来。
之后SQL执行会比较快,且 JDBC可以开启批处理,这个批处理执行非常给力。
劣势:这种方式插入大量数据时,效率非常底下,不推荐。很多时候我们的 SQL 服务器和应用服务器可能并不是同一台,所以必须要考虑网络 IO。
如果网络 IO 比较费时间的话,那么可能会拖慢 SQL 执行的速度。

💍 方案二:利用mybatis的foreach来实现循环插入(不推荐)

insert into t_bank values (?, ?, ?, ?, ?) , (?, ?, ?, ?, ?) , (?, ?, ?, ?, ?)
/**
     * 导入银行信息
     *
     * @param bankList
     * @return java.lang.String
     */
    @Override
    public String importBank(List<TBank> bankList) {
        if (StringUtils.isNull(bankList) || bankList.size() == 0)
        {
            throw new CustomException("导入用户数据不能为空!");
        }
        long start = System.currentTimeMillis();
        tBankMapper.batchInsert(bankList);
        long end = System.currentTimeMillis();
        log.info("数据总耗时:" + (end-start) + "ms" );
        return "Success";
    }
<insert id="batchInsert" parameterType="java.util.List">
        insert into t_bank (
                            bank_id,
                            branch_name,
                            bank_code,
                            contact_line,
                            parent_id,
                            branch_province,
                            branch_province_name,
                            branch_city,
                            branch_city_name)
        values
        <foreach collection="list" item="item" separator=",">
            (
                #{item.bankId},
                #{item.branchName},
                #{item.bankCode},
                #{item.contactLine},
                #{item.parentId},
                #{item.branchProvince},
                #{item.branchProvinceName},
                #{item.branchCity},
                #{item.branchCityName})
        </foreach>
    </insert>

优势:不用频繁访问数据库,一条sql搞定,效率比较高。

劣势:一当数据量太大时,会出现拼接的sql语句超长而执行失败,所以当数据量太大时,也不推荐。

二是 SQL 太长了,甚至可能需要分片后批量处理。

三是无法充分发挥 PreparedStatement 预编译的优势,SQL 要重新解析且无法复用

💍 第三种方案,使用sqlSessionFactory实现批量插入(推荐)

	@Resource
    private SqlSessionFactory sqlSessionFactory;
 /**
     * 导入银行信息
     *
     * @param bankList
     * @param isUpdateSupport
     * @return java.lang.String
     * @author PuWenshuo
     * @date 2023/9/18 11:32
     */
    @Override
    public String importBank(List<TBank> bankList, Boolean isUpdateSupport) {
        if (StringUtils.isNull(bankList) || bankList.size() == 0)
        {
            throw new CustomException("导入用户数据不能为空!");
        }
        String msg="";
        long start = System.currentTimeMillis();
        // 指定分页大小
        int pageSize = 1000; // 每批插入1000条数据
        // 关闭session的自动提交
        SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        try {
            TBankMapper bankMapper = sqlSession.getMapper(TBankMapper.class);
            // 计算总页数
            int totalSize = bankList.size();
            int totalPages = (int) Math.ceil((double) totalSize / pageSize);
            for (int page = 1; page <= totalPages; page++) {
                // 计算当前页的起始和结束索引
                int startIndex = (page - 1) * pageSize;
                int endIndex = Math.min(startIndex + pageSize, totalSize);

                // 获取当前页的数据
                List<TBank> banks = bankList.subList(startIndex, endIndex);
                // 批量插入数据
                tBankMapper.batchInsert(banks);
                // 提交事务
                sqlSession.commit();
            }
            msg="恭喜您,数据已全部导入成功!";
        } catch (Exception e) {
            sqlSession.rollback();
            log.error(e.getMessage());
            msg="很抱歉,导入失败!";
        } finally {
            sqlSession.close();
        }
        long end = System.currentTimeMillis();
        log.info("数据总耗时:" + (end-start) + "ms" );
        return msg;
    }

优势:这种方式可以说是集第一种和第二种方式的优点于一身,既可以提高运行效率,又可以保证大数据量时执行成功,大数据量时推荐使用这种方式。

更多推荐

《扩散模型 从原理到实战》Hugging Face (二)

第二章HuggingFace简介本章无有效内容第三章从零开始搭建扩散模型有时候,只考虑事情最简单的情况反而更有助于理解其工作原理。本章尝试从零开始搭建廓庵模型,我们将从一个简单的扩散模型讲起,了解其不同部分的工作原理,并对比它们与更复杂的结构之间的不同。首先,本章涵盖的知识点:1、退化过程2、什么是UNET模型以及如何

zoneinfo

在Linux系统中,zoneinfo是一个包含了世界各地时区信息的目录,通常位于/usr/share/zoneinfo。这个目录下的子目录和文件名对应了各个时区的名称。例如,/usr/share/zoneinfo/America/Los_Angeles文件就包含了美国洛杉矶的时区信息。你可以通过以下步骤来使用zonei

阿里云产品试用系列-云桌面电脑

无影云电脑(WUYINGWorkspace),是一种易用、安全、高效的云上桌面服务。它支持快速便捷的桌面环境创建、部署、统一管控与运维。无需前期传统硬件投资,帮您快速构建安全、高性能、低成本的企业桌面办公体系。可广泛应用于具有高数据安全管控、高性能计算等要求的安全办公、金融、设计、影视、教育等领域。如上所示,在阿里云官

使用 vue + vant 开发移动端网页

使用vue+vant开发移动端移动端开发的时候我们通常需要进行适配,比如设计图宽度是750px我们为了适应不同的设备,需要将我们设计图上的px转为视口单位vw这里我们采用postcss-px-to-viewport安装:npminstallpostcss-px-to-viewportvant-S新建postcss.co

Rasa:使用大语言模型进行意图分类

Rasa:使用大语言模型进行意图分类在Rasa的最新版本(3.x)中,引入了一种新的意图分类方法,即使用大型语言模型(LLM)和一种称为检索增强生成(RAG)的方法进行意图分类。LLM意图分类器是一种全新的意图分类器,利用大型语言模型(LLM)来对意图进行分类。LLM意图分类器依赖于检索增强生成(RAG)方法,结合了基

爬虫入门基础:深入解析HTTP协议的工作过程

在网络爬虫的学习中,了解HTTP协议的工作过程是非常重要的。HTTP(HypertextTransferProtocol)是一种用于在Web浏览器和服务器之间传输数据的协议,它负责客户端请求和服务器响应之间的通信。本文将详细介绍HTTP协议的工作过程,帮助你深入理解网络爬取的基础知识。让我们一起探索吧!一、HTTP协议

OpenResty使用漏桶算法实现限流

前言其它项目组需要调用接口,添加接口限流,防止项目被狂掉宕机。生产用了openresty,所以在openresty上添加按接口限流,同时,需按照不同接口有不同的限流规则,使用openresty中内置的漏桶算法方式限流。漏桶算法漏桶算法思路简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,

BENTLY 350015 127610-01数字量输入模块

数字输入功能:BENTLY350015127610-01模块通常用于监测和采集数字输入信号,例如开关状态、传感器状态等。多通道:这些模块通常具有多个输入通道,允许同时监测多个数字输入信号。高精度:BENTLY350015127610-01模块提供高精度的信号采集,以确保准确的数据记录和分析。实时监测:具备实时监测功能,

pdf怎么压缩的小一点?pdf文件压缩方法汇总

在日常生活中,我们常常需要处理大量的PDF文件。有时候,这些PDF文件可能因为内容丰富、结构复杂而体积庞大,给我们的存储和传输带来了不便。那么,如何将这些PDF文件压缩得小一点,以便更方便地使用呢?一、嗨格式压缩大师嗨格式压缩大师是一款专业的文件压缩工具,支持图片、视频和PDF等各类文件的压缩,使用它压缩PDF文件,可

机器学习——pca降维/交叉验证/网格交叉验证

1、pca降维:目的是提升模型训练速度定义:使用方法:给训练数据或者测试数据进行降维处理给训练数据降维给测试数据降维:这里1就要用transform,而不是fit_transform,因为之前训练数据降维时特征已经确定,测试数据提取的特征要和训练数据保持一致。2、交叉验证:目的是保证模型的测试数据和训练数据划分清楚,从

[2023.09.12]: Yew应用开发的第一个hook--use_state

Yew的SSR模式推荐使用function_component组件,并且在function_component中使用hooks。其中,我使用到的第一个hook是use_state。use_state的设计意图与React中的useState非常相似,都是为了保存并修改当前的状态。然而,由于Yew是用Rust语言实现的,

热文推荐