• SpringBoot +MyBatis批量插入数据


    💍 背景介绍

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

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

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

    insert into t_bank values (?, ?, ?, ?, ?)
    
    • 1
     /**
         * 导入银行信息
         *
         * @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";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

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

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

    insert into t_bank values (?, ?, ?, ?, ?) , (?, ?, ?, ?, ?) , (?, ?, ?, ?, ?)
    
    • 1
    /**
         * 导入银行信息
         *
         * @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";
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    <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>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

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

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

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

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

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

    	@Resource
        private SqlSessionFactory sqlSessionFactory;
    
    • 1
    • 2
     /**
         * 导入银行信息
         *
         * @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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

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

  • 相关阅读:
    哈希算法(一)Java实现常用的哈希算法
    40个高质量ssm+vue毕设项目分享【源码+论文】(二)
    [pytorch]设备选择以及卷积神经网络的应用
    python.tkinter设计标记语言(转译2-html)
    Zookeeper ACL机制中ProviderRegistry的设计缺陷
    推荐一个前后端分离.NetCore+Angular快速开发框架
    golang 数组 对象排序
    智能终端测试解决方案
    linux cat命令详解,作用,说明
    Banana Pi BPI-W3 RK3588平台驱动调试篇 [ PCIE篇二 ] - PCIE的开发指南
  • 原文地址:https://blog.csdn.net/qq_41596778/article/details/132990502