Mybatis批量插入数据的两种方式总结与对比

 更新时间:2023年01月30日 09:18:26   作者:Neo Yang  
批量插入功能是我们日常工作中比较常见的业务功能之一,下面这篇文章主要给大家介绍了关于Mybatis批量插入数据的两种方式总结与对比的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下

总体描述

软件开发过程中需要批量插入数据的场景有几种:

  • 从离线文件(excel, csv等)导入大批量数据到系统。
  • 从其它系统定时或者人工同步大批量数据到系统。
  • 程序自身的某些算法执行时会生成大批量数据保存到数据库。

上面这些场景都是长时间的处理过程,在软件设计时需要将其设计成带进度展示的异步任务(同步任务微服务有http请求超时的风险)。异步任务可以使用消息框架。

使用批量插入技术能提升数据持久化的性能。用mybatis有两种批量插入数据的方式可选:1. 拼接批量插入多条数据的SQL. 2. 使用Batch Insert技术。

方式一:拼接插入多条数据的SQL

mapper接口代码

    /**
     * 插入数据列表
     *
     * @param dataList 数据列表
     */
    void insertDataList(@Param("list") List<BatchData> dataList);

XML文件配置

    <insert id="batchInsertData" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
        INSERT INTO t_batch_data (
            column1,
            column2,
            column3,
            column4,
            column5,
            column6,
            column7,
            column8,
            column9,
            column10
        ) VALUES
        <foreach item="data" collection="list" separator=",">
            (
                #{data.column1},
                #{data.column2},
                #{data.column3},
                #{data.column4},
                #{data.column5},
                #{data.column6},
                #{data.column7},
                #{data.column8},
                #{data.column9},
                #{data.column10}
            )
        </foreach>
    </insert>

可以看到,XML配置文件使用 foreach 对多条数据做了拼接,Value部分用逗号分隔。拼接后的SQL样式:

INSERT INTO t_batch_data (
            column1,
            column2,
            column3,
            column4,
            column5,
            column6,
            column7,
            column8,
            column9,
            column10
        ) VALUES
          
            (
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?
            )
         , 
            (
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?
            )
         , 
            (
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?,
                ?
            )

可以看到,拼接的SQL长度跟批量插入数据的条数和单条数据的字段数相关。对于像postgres这样限定了参数个数的数据库,需要提前对大批量数据做拆分处理。

下面的示例代码对批量数据按200条一组做拆分,然后再入库。

    public long foreachBatchInsert(@PathVariable("amount") int amount) {
        long beginTime = System.currentTimeMillis();
        List<BatchData> dataList = buildDataList(amount);

        // 大数据分批处理入库
        List<List<BatchData>> dataGroup = ListUtil.splitList(dataList, 200);
        for (List<BatchData> group : dataGroup) {
            batchInsertMapper.insertDataList(group);
        }

        return System.currentTimeMillis() - beginTime;
    }

方式二: 使用Batch Insert技术

Mapper接口代码

    /**
     * 插入单条数据
     *
     * @param data PO数据
     */
    void insertData(@Param("data") BatchData data);

XML文件配置

    <insert id="insertData" useGeneratedKeys="true" keyProperty="data.id" keyColumn="id">
        INSERT INTO t_batch_data (
            column1,
            column2,
            column3,
            column4,
            column5,
            column6,
            column7,
            column8,
            column9,
            column10
        ) VALUES (
            #{data.column1},
            #{data.column2},
            #{data.column3},
            #{data.column4},
            #{data.column5},
            #{data.column6},
            #{data.column7},
            #{data.column8},
            #{data.column9},
            #{data.column10}
        )
    </insert>

映射实例接口和SQL代码与插入单个对象无异。关键代码在应用层。

应用层代码

    public long mybatisBatchInsert(@PathVariable("amount") int amount) {
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false);
        long beginTime = System.currentTimeMillis();

        try {
            BatchInsertMapper insertMapper = session.getMapper(BatchInsertMapper.class);

            List<BatchData> dataList = buildDataList(amount);
            for (BatchData data : dataList) {
                insertMapper.insertData(data);
            }

            session.commit();
            session.clearCache();
        } catch (Exception e) {
            session.rollback();
        } finally {
            session.close();
        }

        return System.currentTimeMillis() - beginTime;
    }

查看打印出执行的SQL语句:

INSERT INTO t_batch_data (
            column1,
            column2,
            column3,
            column4,
            column5,
            column6,
            column7,
            column8,
            column9,
            column10
        ) VALUES (
            ?,
            ?,
            ?,
            ?,
            ?,
            ?,
            ?,
            ?,
            ?,
            ?
        )

拦截StatementHandler的prepare执行方法,可以看到只执行了一次预编译。批量插入不会出现参数个数超限或者SQL语句超长的问题。

对比分析

性能对比

在postgres数据库中新建了一个包含10个text类型字段的表(t_batch_data)验证了一下,插入20万条数据时间都在15秒左右,相差不大。方案1必须做分组(参数个数超过限制);方案二本身是调用的mapper的插入单个对象的接口, 不需要做分批。

应用场景分析

如表字段是固定的,字段数量也不大可以使用方案一;如表字段数量不固定(元数据驱动)推荐使用第二种方案。第二种方案在代码执行到session.commit()时数据才真正入库,如果在这之前使用数据库的数据或者回填的自增ID是有问题的。

实际产品开发过程中,即使采用第二种方案也建议对大数量做分组处理,将单次操作数据库的时间控制在2秒以内。

Demo代码地址: https://github.com/ylforever/elon-postgres.git

总结

到此这篇关于Mybatis批量插入数据的两种方式总结与对比的文章就介绍到这了,更多相关Mybatis批量插入数据内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Mybatis分步查询的实现示例

    Mybatis分步查询的实现示例

    本文主要介绍了Mybatis分步查询的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-07-07
  • Java异常--常见方法--自定义异常--增强try(try-with-resources)详解

    Java异常--常见方法--自定义异常--增强try(try-with-resources)详解

    这篇文章主要介绍了Java异常--常见方法--自定义异常--增强try(try-with-resources)的相关知识,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-03-03
  • Spring中的底层架构核心概念类型转换器详解

    Spring中的底层架构核心概念类型转换器详解

    这篇文章主要介绍了Spring中的底层架构核心概念类型转换器详解,本文结合示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-12-12
  • JAVA字符串占位符使用方法实例

    JAVA字符串占位符使用方法实例

    今天同事又问起类似符串占位符使用的功能,所以下面这篇文章主要给大家介绍了关于JAVA字符串占位符使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-11-11
  • IntelliJ IDEA 2019.1.1 for MAC 下载和注册码激活教程图解

    IntelliJ IDEA 2019.1.1 for MAC 下载和注

    这篇文章主要介绍了IntelliJ IDEA 2019.1.1 for MAC 下载和注册码激活,教程,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-04-04
  • Java 通过AQS实现数据组织

    Java 通过AQS实现数据组织

    这篇文章主要介绍了通过AQS实现数据组织,想了解AQS的同学可以参考下
    2021-04-04
  • Java之Rsync并发迁移数据并校验详解

    Java之Rsync并发迁移数据并校验详解

    这篇文章主要介绍了Java之Rsync并发迁移数据并校验详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Spring与Dubbo搭建一个简单的分布式详情

    Spring与Dubbo搭建一个简单的分布式详情

    这篇文章主要介绍了Spring与Dubbo搭建一个简单的分布式详情,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-08-08
  • java 中ThreadLocal 的正确用法

    java 中ThreadLocal 的正确用法

    这篇文章主要介绍了java 中ThreadLocal 的正确用法的相关资料,需要的朋友可以参考下
    2017-03-03
  • java对接webservice接口的4种方式总结

    java对接webservice接口的4种方式总结

    因工作需要和一个Sap相关系统以WebService的方式进行接口联调,之前仅听过这种技术,但并没有实操过,所以将本次开发进行记录,这篇文章主要给大家介绍了关于java对接webservice接口的4种方式,需要的朋友可以参考下
    2023-10-10

最新评论