Java批量插入数据_数据

💍 背景介绍

在最近的开发过程中,遇到了往数据库中表中插入大量的数据。有一个全国银行各分行的信息,共计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;
    }

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