mybatis以及mybatisplus批量插入问题

news2024/11/16 19:37:53

1. 思路分析:

批量插入是我们日常开放经常会使用到的场景,一般情况下我们也会有两种方案进行实施,如下所示。

方案一 就是用 for 循环循环插入:

优点:JDBC 中的 PreparedStatement 有预编译功能,预编译之后会缓存起来,后面的 SQL 执行会比较快并且JDBC 可以开启批处理,这个批处理执行非常给力。

缺点:很多时候我们的 SQL 服务器和应用服务器可能并不是同一台,所以必须要考虑网络 IO,如果网络 IO 比较费时间的话,那么可能会拖慢
SQL 执行的速度。

再来说第二种方案,就是生成一条 SQL 插入:

优势:这种方案的优势在于只有一次网络 IO,即使分片处理也只是数次网络 IO,所以这种方案不会在网络 IO 上花费太多时间。

缺点一是 SQL 太长了,甚至可能需要分片后批量处理;

缺点二是无法充分发挥 PreparedStatement 预编译的优势,SQL 要重新解析且无法复用;三是最终生成的 SQL
太长了,数据库管理器解析这么长的 SQL 也需要时间。


2. rewriteBatchedStatements=true

在jdbc连接后面加上 rewriteBatchedStatements=true ,加上后才是真正的批量插入。

 jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true

3.使用mybatis批量插入:

方案一:使用foreach进行插入(生成一条 SQL 插入)
mapper文件

   <insert id="save" parameterType="java.util.List">
        INSERT INTO test
        (
        id,
        a,
        b,
        c
        )
        VALUES
        <foreach collection="list" item="item" index="index" separator=",">
            (
            #{item.id},
            #{item.a},
            #{item.b},
            #{item.c}
            )
        </foreach>
    </insert>

调用方法

 @Override
    public void add() {
        //时间 一
        long l = System.currentTimeMillis();
        List<TestEntity> list=new ArrayList<>();
        for (int i=0;i<1000;i++){
            TestEntity testEntity=new TestEntity();
            testEntity.setC(i);
            list.add(testEntity);
        }
       testMapper.save(list);
        //时间 二
        long l1 = System.currentTimeMillis();
        System.out.println("耗时"+(l1-l));
    }

插入了1000条数据,耗时535毫秒。
插入了50000条数据,直接报错。
报错原因是因为我们一条SQL进行插入导致SQL太长
解决办法:
1.修改MySQL配置
2.对新增数据进行分片


方案二:一条条插入

mapper

   <insert id="addUserOneByOne" parameterType="com.ruoyi.system.domain.TestEntity">
    insert into test (id,a,b,c) values (#{id},#{a},#{b},#{c})
    </insert>

测试代码

@Service
public class TestServiceimpl extends ServiceImpl<TestMapper, TestEntity> implements TestService {

    @Autowired
  private   TestMapper testMapper;

    @Autowired
  private SqlSessionFactory sqlSessionFactory;
    
    public void addUserOneByOne(List<TestEntity> users) {
        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
        TestMapper um = session.getMapper(TestMapper.class);
        long startTime = System.currentTimeMillis();
        for (TestEntity user : users) {
            um.addUserOneByOne(user);
        }
        session.commit();
        long endTime = System.currentTimeMillis();
        System.out.println("耗时"+(endTime - startTime));
    }
}

插入了1000条数据,耗时959毫秒。
插入50000条数据,耗时11214毫秒。


对比分析:
如果我们批量插入少部分数据,可以使用方式一,一条SQL进行插入。这样是比较快的。
如果我们插入数据达到,1w条,10来万条,这时建议用方式二进行插入是比较快的。


4. 使用mybatisplus批量插入

使用saveBatch()方法进行批量插入

@Service
public class TestServiceimpl extends ServiceImpl<TestMapper, TestEntity> implements TestService {
    
    @Autowired
  private   TestMapper testMapper;

    @Autowired
  private SqlSessionFactory sqlSessionFactory;

    @Override
    public void add() {
        //时间 一
        long l = System.currentTimeMillis();
        List<TestEntity> list=new ArrayList<>();
        for (int i=0;i<50000;i++){
            TestEntity testEntity=new TestEntity();
            testEntity.setC(i);
            list.add(testEntity);
        }
        saveBatch(list);
        //时间 二
        long l1 = System.currentTimeMillis();
        System.out.println("耗时"+(l1-l));
    }

插入50000条数据,耗时19516毫秒

源码分析

   public boolean saveBatch(Collection<T> entityList, int batchSize) {
        String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
        return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
            sqlSession.insert(sqlStatement, entity);
        });
    }

这里注意 return 中的第三个参数,是一个 lambda 表达式,这也是 MP 中批量插入的核心逻辑,可以看到,MP 先对数据进行分片(默认分片大小是 1000),分片完成之后,也是一条一条的插入。

 public static <E> boolean executeBatch(Class<?> entityClass, Log log, Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
        Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]);
        return !CollectionUtils.isEmpty(list) && executeBatch(entityClass, log, (sqlSession) -> {
            int size = list.size();
            int i = 1;

            for(Iterator var6 = list.iterator(); var6.hasNext(); ++i) {
                E element = var6.next();
                consumer.accept(sqlSession, element);
                if (i % batchSize == 0 || i == size) {
                    sqlSession.flushStatements();
                }
            }

        });
    }

继续查看 executeBatch 方法,就会发现这里的 sqlSession 其实也是一个批处理的 sqlSession,并非普通的 sqlSession。和我们mybatis使用的方法二一致。


5业务场景一对多怎么处理:

比如,如下这种一对多场景。
新增的时候保存都好理解,形成一个数组一起保存。
而修改的时候就有点难处理了,比如我修改了第二条,删除了第三条,这时统一保存应该怎么处理?

在这里插入图片描述

使用 ON DUPLICATE KEY UPDATE (发生主键冲突就更新,没有发生主键冲突就新增)

有时候由于业务需求,可能需要先去根据某一字段值查询数据库中是否有记录,有则更新,没有则插入。这个时候就可以用到ON DUPLICATE key update这个sql语句了

mapper如下所示

   <insert id="save" parameterType="java.util.List">
        INSERT INTO test
        (
        id,
        a,
        b,
        c
        )
        VALUES
        <foreach collection="list" item="item" index="index" separator=",">
            (
            #{item.id},
            #{item.a},
            #{item.b},
            #{item.c}
            )
        </foreach>
        ON DUPLICATE KEY UPDATE
        id=id,
        a = VALUES(a) ,
        b = VALUES(b),
        c = VALUES(c)
    </insert>

或者在使用mybatisplus时,使用saveOrUpdate()方法进行一条数据的新增或更新。 saveOrUpdateBatch()方法进行批量数据的新增或更新。


梅西“消除厄运”卡
在这里插入图片描述

新冠,退退退!
梅老板,冲冲冲!
三星阿根廷,加油!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/83126.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3较vue不同的地方

自定义指令的区别&#xff1a; vue2的写法&#xff1a; Vue.directive(scroll, {}) //scroll是指令名称 vue3的写法&#xff1a; 定义全局的&#xff1a;在main.js文件中定义&#xff1a; createApp(App).directive("hello",{}).use(store).use(router).mount(#…

小程序import及include引用的简单理解

场景&#xff1a;在小程序中&#xff0c;WXML 提供两种文件引用方式import和include 我自己记录下自己的一些简单理解 官方文档&#xff1a;引用 | 微信开放文档 第一&#xff1a;import import&#xff0c;就是可以引入自定义指定的template模板 比如&#xff1a;我在import页…

stm32f767之ADC

一&#xff0c;基本介绍 1&#xff0c;ADC时钟。 ADC时钟一般常用来自于经可编程预分频器分频的APB2 时钟&#xff0c;该预分频器允许ADC 在fPCLK2/2、 /4、/6 或/8 下工作。ADCCLK 的最大值限制。2&#xff0c;ADC通道。 有16 条复用通道。我的理解是每个ADC&#xff08;1&…

气泡水位计安装示意图 气泡水位计工作原理

气泡式水位计测量精度高&#xff0c;免气瓶&#xff0c;免测井&#xff0c;免维护&#xff0c;抗振动&#xff0c;寿命长&#xff0c;特别适用于流动水体、大中小河流等水深比较大的场合。具有安装简单&#xff0c;操作、组网灵活&#xff0c;尤其是无井水位测量最理想的水位监…

城市燃气系统安全解决方案

汽车制造业 MES系统 DNC系统 生产 安全域1 管理层 工控安全隔离装置 交换机 安全配置核查系统 HMI 历史数据库 运行监控系统 实时数据库 打印机过程 安全域2 监控层 工控漏洞扫描系统 安全交换机 工控安全审计系统 工控入侵检测系统工程师站 A 操作员站 A 实时数据库A 操作员站…

[附源码]Python计算机毕业设计SSM基于的冠状病毒疫情防控资讯交流推荐网站(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

stream_open函数分析

在讲 stream_open() 函数之前&#xff0c;需要先了解 stream_open() 里面使用到的一些基本的数据结构。如下&#xff1a; 第一个数据结构是 struct VideoState &#xff0c;VideoState 可以说是播放器的全局管理器。字段非常多&#xff0c;时钟&#xff0c;队列&#xff0c;解…

Android 11 的状态栏的隐藏

概述 Android 11 的状态栏与导航栏较之前的版本有较大的差异, 在Android 7.0 SystemUI 状态/导航栏的隐藏与显示中所描述的部分内容已不再适用. 比如, 自动隐藏的时间, 隐藏的动画, 较之前的版本已面目全非, 本文将对隐藏状态栏部分的内容进行一些补充. APP如何隐藏状态栏 参…

Yield Guild Games 成功举办首届 SubDAO 峰会

Yield Guild Games&#xff08;YGG&#xff09;于 2022 年 11 月 18 日在菲律宾马尼拉举行了第一届 SubDAO 峰会。 SubDAO 峰会与菲律宾 Web3 狂欢节两个活动同时举行&#xff0c;为 YGG 的区域 SubDAO 提供了在 Web3 应用中心——菲律宾进行面对面交流的机会。此次活动旨在传达…

运维开发实践 - helm

1. helm介绍 helm 是一个用于管理部署在kubernetes上的应用的工具 使用要求&#xff1a;一个Kubernetes集群 2.下载安装 Helm Github Download Helm Huawei Source 按照自己的操作系统版本下载相应的helm压缩包 并将helm添加到环境变量中; # 检查是否安装成功 helm version…

read_thread解复用线程分析

read_thread() 线程的主要作用从 MP4 里面读取 AVPacket&#xff0c;然后丢进去 PacketQueue 队列。所以需要先学习一下 strcut PacketQueue 跟 struct MyAVPacketList 数据结构。如下&#xff1a; typedef struct MyAVPacketList {AVPacket *pkt;int serial; } MyAVPacketLis…

html文件里怎么引用vue组件?

这里我们使用 http-vue-loader 来实现&#xff1a;https://www.npmjs.com/package/http-vue-loader Load .vue files directly from your html/js. No node.js environment, no build step. 我做了个demo如下&#xff1a; html文件里面写下面的代码 <!DOCTYPE html> &l…

计算机研究生就业方向之当老师(中小学)

我一直跟学生们说你考计算机的研究生之前一定要想好你想干什么&#xff0c;如果你只是转码&#xff0c;那么你不一定要考研&#xff0c;至少以下几个职位研究生是没有啥优势的&#xff1a; 1&#xff0c;软件测试工程师&#xff08;培训一下就行&#xff09; 2&#xff0c;前…

股票购买接口系统怎么使用vn.py进行量化策略?

一般情况下&#xff0c;股票购买接口系统主要是可以运用在股票量化交易系统开发的一个大方向&#xff0c;也就是说&#xff0c;股票购买接口系统是根据这些量化的特点来开发的&#xff0c;就比如使用vn.py进行量化策略&#xff0c;在这方面&#xff0c;对交易者进行量化分析也起…

Web前端105天-day-41-JSCORE

JSCORE01 目录 前言 一、声明提升 二、宿主 window 三、断点功能 四、匿名函数解决全局污染 五、作用域链 六、闭包 七、私有 八、arguments 九、函数重载 十、方括号属性语法 十一、重载练习 十二、this 总结 前言 JSCORE01学习开始 一、声明提升 报错方案: 让…

走进SpringCloud微服务

微服务概述一、注册中心&#xff1a;Eureka ⭐⭐⭐1.1 原理1.2 代码二、负载均衡&#xff1a;Ribbon ⭐三、远程调用&#xff1a;Feigh ⭐⭐⭐3.1 原理3.2 代码四、熔断限流&#xff1a;Hystrix ⭐⭐⭐4.1线程池策略4.2 信号量隔离策略4.3 方法降级4.4 断路器、熔断器五、网关&…

MongoDB和MongoTemplate对于嵌套数据的判空查询

前言&#xff1a; 不知道有没有和小名一样&#xff0c;接触MongDB时间不长的小伙伴。由于MongoDB是以文档形式存储数据的&#xff0c;所以其中的数据类型相对MySql或者Oracle关系型数据库丰富一些&#xff08;MongoDB是NoSQL数据库这里比较不是很准确&#xff09; 我们在关系…

Dropout方法原理和使用方法

来源&#xff1a;投稿 作者&#xff1a;梦飞翔 编辑&#xff1a;学姐 为什么提出这种方法&#xff1f; 神经网络在训练过程中&#xff0c;由于正负样本不均衡、样本特征显著性不足、训练参数不合适等原因会导致发生过拟合现象&#xff0c;即模型参数陷入局部最优&#xff0c;仅…

QT6操作连接mysql数据库方法_增删改查

QT6操作mysql方法_增删改查 mysql数据库搭建相关方法&#xff1a; MySQL - 随笔分类 - txwtech - 博客园https://www.cnblogs.com/txwtech/category/1973054.htmlMySQL解压版配置方法 MySQL解压版配置方法 - txwtech - 博客园1.下载 https://downloads.mysql.com/archives/co…

【知识学习】C++QT配置opencv遇到的坑

最近要搞图像&#xff0c;老师说尽量用C&#xff0c;就开始研究配置opencv 当然&#xff0c;说在前面&#xff0c;C的比python的要麻烦特别多&#xff0c;所以如果不是必要的话&#xff0c;建议用python pip配opencv吧 C麻烦就在于要自己在本地编译一遍才能跑&#xff0c;直接…