MyBatis原生批量插入的坑与解决方案!

353e1bc59b9f3be58c495390b922fc5c.png

作者 | 王磊

来源 | Java中文社群(ID:javacn666)

转载请联系授权(微信ID:GG_Stone)

前面的文章咱们讲了 MyBatis 批量插入的 3 种方法:循环单次插入、MyBatis Plus 批量插入、MyBatis 原生批量插入,详情请点击《MyBatis 批量插入数据的 3 种方法!》

但之前的文章也有不完美之处,原因在于:使用 「循环单次插入」的性能太低,使用「MyBatis Plus 批量插入」性能还行,但要额外的引入 MyBatis Plus 框架,使用「MyBatis 原生批量插入」性能最好,但在插入大量数据时会导致程序报错,那么,今天咱们就会提供一个更优的解决方案。

原生批量插入的“坑”

首先,我们来看一下 MyBatis 原生批量插入中的坑,当我们批量插入 10 万条数据时,实现代码如下:

import com.example.demo.model.User;
import com.example.demo.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.util.ArrayList;
import java.util.List;@SpringBootTest
class UserControllerTest {// 最大循环次数private static final int MAXCOUNT = 100000;@Autowiredprivate UserServiceImpl userService;/*** 原生自己拼接 SQL,批量插入*/@Testvoid saveBatchByNative() {long stime = System.currentTimeMillis(); // 统计开始时间List<User> list = new ArrayList<>();for (int i = 0; i < MAXCOUNT; i++) {User user = new User();user.setName("test:" + i);user.setPassword("123456");list.add(user);}// 批量插入userService.saveBatchByNative(list);long etime = System.currentTimeMillis(); // 统计结束时间System.out.println("执行时间:" + (etime - stime));}
}

核心文件 UserMapper.xml 中的实现代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.demo.mapper.UserMapper"><insert id="saveBatchByNative">INSERT INTO `USER`(`NAME`,`PASSWORD`) VALUES<foreach collection="list" separator="," item="item">(#{item.name},#{item.password})</foreach></insert></mapper>

当我们开心地运行以上程序时,就出现了以下的一幕:

ae6bc4b918b7ce56749da31e73fe78b3.png沃,程序竟然报错了!

这是因为使用 MyBatis 原生批量插入拼接的插入 SQL 大小是 4.56M,而默认情况下 MySQL 可以执行的最大 SQL 为 4M,那么在程序执行时就会报错了。

解决方案

以上的问题就是因为批量插入时拼接的 SQL 文件太大了,所以导致 MySQL 的执行报错了。那么我们第一时间想到的解决方案就是将大文件分成 N 个小文件,这样就不会因为 SQL 太大而导致执行报错了。也就是说,我们可以将待插入的 List 集合分隔为多个小 List 来执行批量插入的操作,而这个操作过程就叫做 List 分片。

有了处理思路之后,接下来就是实操了,那如何对集合进行分片操作呢?

分片操作的实现方式有很多种,这个我们后文再讲,接下来我们使用最简单的方式,也就是 Google 提供的 Guava 框架来实现分片的功能。

分片 Demo 实战

要实现分片功能,第一步我们先要添加 Guava 框架的支持,在 pom.xml 中添加以下引用:

<!-- google guava 工具类 -->
<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.0.1-jre</version>
</dependency>

接下来我们写一个小小的 demo,将以下 7 个人名分为 3 组(每组最多 3 个),实现代码如下:

import com.google.common.collect.Lists;import java.util.Arrays;
import java.util.List;/*** Guava 分片*/
public class PartitionByGuavaExample {// 原集合private static final List<String> OLD_LIST = Arrays.asList("唐僧,悟空,八戒,沙僧,曹操,刘备,孙权".split(","));public static void main(String[] args) {// 集合分片List<List<String>> newList = Lists.partition(OLD_LIST, 3);// 打印分片集合newList.forEach(i -> {System.out.println("集合长度:" + i.size());});}
}

以上程序的执行结果如下:

f949feef2370c127a2f63c7923b69ed0.png从上述结果可以看出,我们只需要使用 Guava 提供的 Lists.partition 方法就可以很轻松的将一个集合进行分片了。

原生批量插入分片实现

那接下来,就是改造我们的 MyBatis 批量插入代码了,具体实现如下:

@Test
void saveBatchByNativePartition() {long stime = System.currentTimeMillis(); // 统计开始时间List<User> list = new ArrayList<>();// 构建插入数据for (int i = 0; i < MAXCOUNT; i++) {User user = new User();user.setName("test:" + i);user.setPassword("123456");list.add(user);}// 分片批量插入int count = (int) Math.ceil(MAXCOUNT / 1000.0); // 分为 n 份,每份 1000 条List<List<User>> listPartition = Lists.partition(list, count);// 分片批量插入for (List<User> item : listPartition) {userService.saveBatchByNative(item);}long etime = System.currentTimeMillis(); // 统计结束时间System.out.println("执行时间:" + (etime - stime));
}

执行以上程序,最终的执行结果如下:

14d99085c1d33b59d6dc2307a8662ef7.png

从上图可以看出,之前批量插入时的异常报错不见了,并且此实现方式的执行效率竟比 MyBatis Plus 的批量插入的执行效率要高,MyBatis Plus 批量插入 10W 条数据的执行时间如下:

fc3ebfcf800b382818a0faea344b1a8f.png

总结

本文我们演示了 MyBatis 原生批量插入时的问题:可能会因为插入的数据太多从而导致运行失败,我们可以通过分片的方式来解决此问题,分片批量插入的实现步骤如下:

  1. 计算出分片的数量(分为 N 批);

  2. 使用 Lists.partition 方法将集合进行分片(分为 N 个集合);

  3. 循环将分片的集合进行批量插入的操作。

e0db0453aeb5320573417a407391edf9.gif

往期推荐

310ebdbc4c9f26244e2ede250b2f3f3e.png

MyBatis 批量插入数据的 3 种方法!


9c87eafdca7bbba14684a6152d242efa.png

MyBatis Plus 批量数据插入功能,yyds!


f580758173002fec247ee27bfd1424aa.png

如何给SpringBoot配置轻松加密?


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

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

相关文章

系统结构图 数据结构_数据结构图简介

系统结构图 数据结构What you are going to learn? 你要学什么&#xff1f; In this article, we learn about the introduction to Graphs in Data Structure and Algorithm. 在本文中&#xff0c;我们将了解图在数据结构和算法中的介绍 。 What are the components in Gra…

Matlab仿真PID控制(带M文件、simulink截图和参数分析)

文章目录0.符号说明1.如何根据连续系统建立差分方程1.1.获取连续系统的传递函数1.2.获取离散系统的传递函数1.3.转换为差分方程2.基本PID控制原理3.比较PID输出&#xff0c;分析参数产生的影响4.改进PID算法&#xff08;遇限削弱积分法&#xff09;5.simulink仿真0.符号说明 y…

再见 Postman!Apifox 才是 YYDS!

作为开软件开发从业者&#xff0c;API 调试是必不可少的一项技能&#xff0c;在这方面 Postman 做的非常出色。但是在整个软件开发过程中&#xff0c;API 调试只是其中的一部分&#xff0c;还有很多事情 Postman 无法完成&#xff0c;或者无法高效完成&#xff0c;比如&#xf…

Matlab【可视化作图】绘制线电压相电压辅助线

目录引言绘图原理采点绘图设置坐标轴标尺引言 学习电力电子的同学可能在私下里练习的时候非常需要三相线电压和相电压的辅助线。最近我随便找了一本书把Matlab可视化编程恶补了一下&#xff0c;给大家介绍一下这个波形辅助线是怎么做的。 三相线电压辅助线就是一组相位相差60的…

SpringBoot实现Excel导入导出,好用到爆,POI可以扔掉了!

在我们平时工作中经常会遇到要操作Excel的功能&#xff0c;比如导出个用户信息或者订单信息的Excel报表。你肯定听说过POI这个东西&#xff0c;可以实现。但是POI实现的API确实很麻烦&#xff0c;它需要写那种逐行解析的代码&#xff08;类似Xml解析&#xff09;。今天给大家推…

Facebook升级到MySQL 8.0付出的代价

近日&#xff0c;Facebook 官博公布了他们的数据库版本从 MySQL 5.6 升级到了 MySQL 8.0&#xff0c;并且在官博记录了复盘详细的升级过程。Facebook 称&#xff0c;他们最近的一次大版本升级到 MySQL 5.6 花了一年多时间才完成&#xff0c;还在 5.6 版上开发 LSM 树存储引擎&a…

Matlab制作朱利表

朱利判据 其中 {bn−kan−k−ana0∗akcn−kbn−k−bnb0∗bk...qn−kpn−k−pnp0∗pk\begin{cases} b_{n-k}a_{n-k}-\frac{a_n}{a_0}*a_k\\ c_{n-k}b_{n-k}-\frac{b_n}{b_0}*b_k\\ ...\\ q_{n-k}p_{n-k}-\frac{p_n}{p_0}*p_k \end{cases}⎩⎪⎪⎪⎨⎪⎪⎪⎧​bn−k​an−k​−a0…

高并发下秒杀商品,必须知道的9个细节

高并发下如何设计秒杀系统&#xff1f;这是一个高频面试题。这个问题看似简单&#xff0c;但是里面的水很深&#xff0c;它考查的是高并发场景下&#xff0c;从前端到后端多方面的知识。秒杀一般出现在商城的促销活动中&#xff0c;指定了一定数量&#xff08;比如&#xff1a;…

最小拍控制系统详细解读(阶跃输入+速度输入2个案例)【Simulink仿真】

目录索引1.符号说明与结构框图2.最小拍控制系统构造原则2.1数字控制器D(z)的构造3.简单控制对象的最小拍控制器设计3.1阶跃输入3.2速度输入1.符号说明与结构框图 y(k)——系统响应输出的离散值u(k)——数字PID控制输出的离散值r(k)——期望输出的离散值&#xff08;事先已知&a…

SpringBoot官方热部署和远程调试神器,真带劲!

平时使用SpringBoot开发应用时&#xff0c;修改代码后需要重新启动才能生效。如果你的应用足够大的话&#xff0c;启动可能需要好几分钟。有没有什么办法可以加速启动过程&#xff0c;让我们开发应用代码更高效呢&#xff1f;今天给大家推荐一款SpringBoot官方的热部署工具spri…

【Python】输入任意个数元素并保存至列表

目录1.导入任意个数元素到列表1.1.编程思路1.2.代码片2.查找一个重复元素在列表中的所有位置2.1.编程思路2.2代码片1.导入任意个数元素到列表 1.1.编程思路 输入未知个数的元素需要用列表来存储&#xff0c;由于Python具有内存的动态分配能力&#xff0c;列表不需要手动动态分…

MySQL 性能优化的 9 种姿势,面试再也不怕了!

大家好&#xff0c;我是磊哥&#xff01;今天给大家分享一些简单好用的数据库优化方式&#xff01;1、选择最合适的字段属性Mysql是一种关系型数据库&#xff0c;可以很好地支持大数据量的存储&#xff0c;但是一般来说&#xff0c;数据库中的表越小&#xff0c;在它上面执行的…

Excel的规划求解【详细步骤】

本文目录1.说明2.准备加载项步骤1步骤2步骤33.线性规划问题步骤4步骤5步骤61.说明 使用Lingo程序也可以实现线性规划、非线性规划以及0-1规划&#xff0c;但是在缺少Lingo程序的情况下&#xff0c;我们使用Excel照样可以很容易地完成。在这里我给大家提供了解决此类问题的详细…

4 种方法!检查字符串是否为合法的日期格式

哈喽大家好&#xff0c;今天咱们来讲一下&#xff0c;Java 中如何检查一个字符串是否是合法的日期格式&#xff1f;为什么要检查时间格式&#xff1f;后端接口在接收数据的时候&#xff0c;都需要进行检查。检查全部通过后&#xff0c;才能够执行业务逻辑。对于时间格式&#x…

【Matlab】根据图生成带权邻接矩阵,并求出最短路径

目录图的简介无向图&#xff08;Graph&#xff09;生成带权邻接矩阵求两点最短路径有向图&#xff08;Digraph&#xff09;生成带权邻接矩阵求最短路径图的简介 图是拓扑学中的一个重要概念&#xff0c;分为无向图和有向图两种。图有两个重要属性&#xff0c;即点&#xff08;…

阿里二面:为什么要分库分表?

在高并发系统当中&#xff0c;分库分表是必不可少的技术手段之一&#xff0c;同时也是BAT等大厂面试时&#xff0c;经常考的热门考题。你知道我们为什么要做分库分表吗&#xff1f;这个问题要从两条线说起&#xff1a;垂直方向 和 水平方向。1 垂直方向垂直方向主要针对的是业务…

Java 中 List 分片的 5 种方法!

作者 | 王磊来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09;前些天在实现 MyBatis 批量插入时遇到了一个问题&#xff0c;当批量插入的数据量比较大时&#xff0c;会导致程序执行报错&a…

Matlab仿真炮弹飞行轨迹——探究射弹参数对飞行轨迹的影响

目录1.分析炮弹受力2.设定参数并仿真3.通过仿真寻找最佳射弹速度3.1.射弹角度的影响3.2.射弹速率的影响3.3.炮弹属性和空气的影响3.3.1.空气阻力系数的影响3.3.2.炮弹质量的影响1.分析炮弹受力 假设炮弹在飞行过程中可以看成质点&#xff0c;运动时仅考虑初始速度、重力加速度…

50行代码,搞定敏感数据读写!

每天早上七点三十&#xff0c;准时推送干货一、介绍在实际的软件系统开发过程中&#xff0c;由于业务的需求&#xff0c;在代码层面实现数据的脱敏还是远远不够的&#xff0c;往往还需要在数据库层面针对某些关键性的敏感信息&#xff0c;例如&#xff1a;身份证号、银行卡号、…

【Python】导入资源管理器的文件列表(计算文件和文件夹大小)

文章目录1.按照扩展名进行分类2.导出文件的大小3.计算文件夹大小4.分类到字典5.完整代码及效果1.按照扩展名进行分类 使用Python查询一个路径下的所有文件可以借助glob模块以及os模块。 在导入文件列表之前&#xff0c;我们需要指定我们的操作目录&#xff0c;操作目录一经指定…