SpringBoot整合mybatisPlus实现批量插入并获取ID详解

本文主要是介绍SpringBoot整合mybatisPlus实现批量插入并获取ID详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBoot整合mybatisPlus实现批量插入并获取ID详解》这篇文章主要为大家详细介绍了SpringBoot如何整合mybatisPlus实现批量插入并获取ID,文中的示例代码讲解详细...

背景:需要实现批量插入并且得到插入后的ID。

使用for循环进行insert这里就不说了,在海量数据下其性能是最慢的。数据量小的情况下,没什么区别。

【1】saveBatch(一万条数据总耗时:2478ms)

mybatisplus扩展包提供的:com.baomidou.mybatisplus.extension.service.IService#saveBatch(Java.util.Collection<T>)

测试代码:

@Test
 public void testBatch1(){
     List<SysFile> list=new ArrayList<>();
     list.add(new SysFile().setFileName("fiel1"));
     list.add(new SysFile().setFileName("fiel2"));
     list.add(new SysFile().setFileName("fiel3"));
     list.add(new SysFile().setFileName("fiel4"));
     list.add(new SysFile().setFileName("fiel5"));
     list.add(new SysFile().setFileName("fiel6"));
     fileService.saveBatch(list);
     System.out.println(list);
 }

我们分析其实现原理如下:com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveBatch

@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveBatch(Collection<T> entityList, int batchSize) {
    String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE)China编程;
    int size = entityList.size();
    executeBatch(sqlSession -> {
        int i = 1;
        for (T entity : entityList) {
            sqlSession.insert(sqlStatement, entity);
            if ((i % batchSize == 0) || i == size) {
                sqlSession.flushStatements();
            }
            i++;
        }
    });
    return true;
}

其实也就是一条条插入。

SpringBoot整合mybatisPlus实现批量插入并获取ID详解

【2】集合方式foreach(一万条数据总耗时:474ms)

SysFileMapper 自定义方法batchSaveFiles

public interface SysFileMapper extends BaseMapper<SysFile> {
    int batchSaveFiles(List<SysFile> entityList);
}

XML实现

<insert id="batchSaveFiles">
    insert  into tb_sys_file (file_name) values
    <foreach collection="list" item="item" separator=",">
        (#{item.fileName})
    </foreach>
</insert>

测试代码:

@Test
public void testBatch2(){
    List<SysFile> list=new ArrayList<>();
    list.add(new SysFile().setFileName("fiel1"));
    list.add(new SysFile().setFileName("fiel2"));
    list.add(new SysFile().setFileName("fiel3"));
    list.add(new SysFile().setFileName("fiel4"));
    list.add(new SysFile().setFileName("fiel5"));
    list.add(new SysFile().setFileName("fiel6"));
    fileMapperandroid.batchSaveFiles(list);
    System.out.println(list);
}

测试结果:

SpringBoot整合mybatisPlus实现批量插入并获取ID详解

注意:这种方式得不到ID哦!

【3】MyBatis-Plus提供的InsertBatchSomeColumn方法(一万条数据总耗时:690ms)

这里mybatisplus版本是3.3.0。

编写mysqlInjector

public class MySqlInjector extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass);
      js  //更新时自动填充的字段,不用插入值
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        return methodList;
    }
}

为什么这里不用下面第二行的方式呢?

methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
methodList.add(new InsertBatchSomeColumn());

这两行代码分别添加了两个 InsertBatchSomeColumn 方法到 methodList 中。

第一个 InsertBatchSomeColumn 方法使用了一个 Lambda 表达式作为参数,该表达式用于过滤字段,只保留那些 getFieldFill 属性不是 FieldFill.UPDATE 的字段。

第二个 InsertBatchSomeColumn 方法没有参数,表示不进行任何过滤,直接插入所有字段。

注入到配置类

@EnableTransactionManagement
@MapperScan({"com.enodeb.mapper"})
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MySqlInjector sqlInjector() {
        return new MySqlInjector();
    }
}    

SysFileMapper 自定义方法

public interface SysFileMapper extends BaseMapper<SysFile> {

    int insertBatchSomeColChina编程umn(List<SysFile> entityList);

测试代码:

@Test
public void testBatch3(){
    List<SysFile> list=new ArrayList<>();
    list.add(new SysFile().setFileName("fiel1"));
    list.add(new SysFile().setFileName("fiel2"));
    list.add(new SysFile().setFileName("fiel3"));
    list.add(new SysFile().setFileName("fiel4"));
    list.add(new SysFile().setFileName("fiel5"));
    list.add(new SysFile().setFileName("fiel6"));
    fileMapper.insertBatchSomeColumn(list);
    System.out.println(list);
}

测试结果

SpringBoot整合mybatisPlus实现批量插入并获取ID详解

这里不仅实现了【2】的效果,还可以得到插入后的ID。

【4】假设一万条/十万条数据的情况下,执行时间是多少

策略一万条十万条
方式一2478ms20745ms
方式二474ms2904ms
方式三690ms8339ms

① 方式一

@Test
    public void testBatch1(){
        long start=System.currentTimeMillis();
        List<SysFile> list=new ArrayList<>();
        SysFile sysFile;
        for(int i=0;i<10000;i++){
            sysFile=new SysFile();
            sysFile.setFileName("file"+i);
            list.add(sysFile);
        }
        fileService.saveBatch(list);
        long end=System.currentTimeMillis();
        System.out.println("一万条数据总耗时:"+(end-start)+"ms");
    }

一万条数据总耗时:2478ms

十万条数据总耗时:20745ms

② 方式二

@Test
public void testBatch2(){
     long start=System.currentTimeMillis();
     List<SysFile> list=new ArrayList<>();
     SysFile sysFile;
     for(int i=0;i<10000;i++){
         sysFile=new SysFile();
         sysFile.setFileName("file"+i);
         list.add(sysFile);
     }
     fileMapper.batchSaveFiles(list);
     long end=System.currentTimeMillis();
     System.out.println("一万条数据总耗时:"+(end-start)+"ms");
 }

一万条数据总耗时:474ms

十万条数据总耗时:2904ms

③ 方式三

@Test
public void testBatch3(){
    long start=System.currentTimeMillis();
    List<SysFile> list=new ArrayList<>();
    SysFile sysFile;
    for(int i=0;i<10000;i++){
        sysFile=new SysFile();
        sysFile.setFileName("file"+i);
        list.add(sysFile);
    }
    fileMapper.insertBatchSomeColumn(list);
    long end=System.currentTimeMillis();
    System.out.println("一万条数据总耗时:"+(end-start)+"ms");
}

一万条数据总耗时:690ms

十万条数据总耗时:8339ms

【5】百万条数据的情况下进行优化

方式二、方式三都是拼接为一条SQL,也就说有多少直接全部一次性插入,这就可能会导致最后的 sql 拼接语句特别长,超出了mysql 的限制。

这是什么意思呢?以MySQL为例,我们是需要考虑 max_allowed_packet 这个属性配置大小。其决定了你最大可以单次发送包的大小,这里可以修改为64M也就是 67108864。

但是这个不是最优解,最优解应该是控制每次插入的数量,比如一万条插入一次。

    @Test
    public void testBatch4(){
        List<SysFile> list=new ArrayList<>();
        SysFile sysFile;
        for(int i=0;i<100000;i++){
            sysFile=new SysFile();
            sysFile.setFileName("file"+i);
            list.add(sysFile);
        }
        //设置每批次插入多少条数据
        int batchSize=10000;
        int count = (list.size() + batchSize - 1) / batchSize; // 计算总批次数量,确保最后一个批次也能处理
        //保存单批提交的数据集合
        List<SysFile> oneBatchList = new ArrayList<>(batchSize); // 预分配容量

​​​​​​​        for (int i = 0; i < count; i++) {
            int startIndex = i * batchSize;
            int endIndex = Math.min(startIndex + batchSize, list.size());
            oneBatchList.addAll(list.subList(startIndex, endIndex));
            fileMapper.insertBatchSomeColumn(oneBatchList);
            oneBatchList.clear(); // 清空集合以备下次循环使用
        }编程
    }

【TIPS】

为了确保批量插入的高效性,还需要进行一些配置和优化。例如,在application.yml中配置数据库连接时,可以开启MySQL的批处理模式

【rewriteBatchedStatements=true】:

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/testBtach?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver

此外还可以考虑使用jdbcTemplate.batchUpdate、Spring Batch来实现(这两种未测试)。

到此这篇关于SpringBoot整合mybatisPlus实现批量插入并获取ID详解的文章就介绍到这了,更多相关SpringBoot整合mybatisPlus插入ID内容请搜索编程China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于SpringBoot整合mybatisPlus实现批量插入并获取ID详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154406

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

MySQL数据库双机热备的配置方法详解

《MySQL数据库双机热备的配置方法详解》在企业级应用中,数据库的高可用性和数据的安全性是至关重要的,MySQL作为最流行的开源关系型数据库管理系统之一,提供了多种方式来实现高可用性,其中双机热备(M... 目录1. 环境准备1.1 安装mysql1.2 配置MySQL1.2.1 主服务器配置1.2.2 从

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

MyBatis常用XML语法详解

《MyBatis常用XML语法详解》文章介绍了MyBatis常用XML语法,包括结果映射、查询语句、插入语句、更新语句、删除语句、动态SQL标签以及ehcache.xml文件的使用,感兴趣的朋友跟随小... 目录1、定义结果映射2、查询语句3、插入语句4、更新语句5、删除语句6、动态 SQL 标签7、ehc

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node