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

作者 | 王磊

来源 | 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;     @Autowired     private UserServiceImpl userService;     /**      * 原生自己拼接 SQL,批量插入      */     @Test     void 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 中的实现代码如下:

<!-- 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());         });     } }

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

从上述结果可以看出,我们只需要使用 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)); }

执行以上程序,最终的站群服务器执行结果如下:

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

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

计算出分片的数量(分为 N 批); 使用 Lists.partition 方法将集合进行分片(分为 N 个集合); 循环将分片的集合进行批量插入的操作。
域名
上一篇:4、待所有域名查询结束后可在右侧点击导出结果,即可以excel的文件方式将查询到的结果导出。
下一篇:4、企业无形资产:通用网站已成为企业网络知识产权的重要组成部分,属于企业的无形资产,也有助于提升企业的品牌形象和技术领先形象。它是企业品牌资产不可或缺的一部分。