千家信息网

MyBatis批量插入数据的方法有哪些

发表于:2025-11-14 作者:千家信息网编辑
千家信息网最后更新 2025年11月14日,本篇内容介绍了"MyBatis批量插入数据的方法有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
千家信息网最后更新 2025年11月14日MyBatis批量插入数据的方法有哪些

本篇内容介绍了"MyBatis批量插入数据的方法有哪些"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

目录
  • 前言

  • 准备工作

  • 1.循环单次插入

  • 2.MP 批量插入

    • ① 控制器实现

    • ② 业务逻辑层实现

    • ③ 数据持久层实现

    • MP 性能测试

    • MP 源码分析

  • 3.原生批量插入

    • ① 业务逻辑层扩展

    • ② 数据持久层扩展

    • ③ 添加 UserMapper.xml

    • 原生批量插入性能测试

    • 缺点分析

      • 解决方案


前言

先来简单说一下 3 种批量插入功能分别是:

  1. 循环单次插入;

  2. MP 批量插入功能;

  3. 原生批量插入功能。

准备工作

开始之前我们先来创建数据库和测试数据,执行的 SQL 脚本如下:

-- ------------------------------ 创建数据库-- ----------------------------SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;DROP DATABASE IF EXISTS `testdb`;CREATE DATABASE `testdb`;USE `testdb`;-- ------------------------------ 创建 user 表-- ----------------------------DROP TABLE IF EXISTS `user`;CREATE TABLE `user`  (  `id` int(11) NOT NULL AUTO_INCREMENT,  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,  `password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,  `createtime` datetime NULL DEFAULT CURRENT_TIMESTAMP,  PRIMARY KEY (`id`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 6 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;-- ------------------------------ 添加测试数据-- ----------------------------INSERT INTO `user` VALUES (1, '赵云', '123456', '2021-09-10 18:11:16');INSERT INTO `user` VALUES (2, '张飞', '123456', '2021-09-10 18:11:28');INSERT INTO `user` VALUES (3, '关羽', '123456', '2021-09-10 18:11:34');INSERT INTO `user` VALUES (4, '刘备', '123456', '2021-09-10 18:11:41');INSERT INTO `user` VALUES (5, '曹操', '123456', '2021-09-10 18:12:02');SET FOREIGN_KEY_CHECKS = 1;

1.循环单次插入

接下来我们将使用 Spring Boot 项目,批量插入 10W 条数据来分别测试各个方法的执行时间。

循环单次插入的(测试)核心代码如下:

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;@SpringBootTestclass UserControllerTest {    // 最大循环次数    private static final int MAXCOUNT = 100000;    @Autowired    private UserServiceImpl userService;    /**     * 循环单次插入     */    @Test    void save() {        long stime = System.currentTimeMillis(); // 统计开始时间        for (int i = 0; i < MAXCOUNT; i++) {            User user = new User();            user.setName("test:" + i);            user.setPassword("123456");            userService.save(user);        }        long etime = System.currentTimeMillis(); // 统计结束时间        System.out.println("执行时间:" + (etime - stime));    }}

运行以上程序,花费了 88574 毫秒

2.MP 批量插入

MP 批量插入功能核心实现类有三个:UserController(控制器)、UserServiceImpl(业务逻辑实现类)、UserMapper(数据库映射类),它们的调用流程如下:

注意此方法实现需要先添加 MP 框架,打开 pom.xml 文件添加如下内容:

    com.baomidou    mybatis-plus-boot-starter    mybatis-plus-latest-version

注意:mybatis-plus-latest-version 表示 MP 框架的最新版本号,可访问 mvnrepository.com/artifact/co… 查询最新版本号,但在使用的时候记得一定要将上面的 "mybatis-plus-latest-version"替换成换成具体的版本号,如 3.4.3 才能正常的引入框架。

更多 MP 框架的介绍请移步它的官网:baomidou.com/guide/

① 控制器实现

import com.example.demo.model.User;import com.example.demo.service.impl.UserServiceImpl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;import java.util.List;@RestController@RequestMapping("/u")public class UserController {    @Autowired    private UserServiceImpl userService;    /**     * 批量插入(自定义)     */    @RequestMapping("/mysavebatch")    public boolean mySaveBatch(){        List list = new ArrayList<>();        // 待添加(用户)数据        for (int i = 0; i < 1000; i++) {            User user = new User();            user.setName("test:"+i);            user.setPassword("123456");            list.add(user);        }        return userService.saveBatchCustom(list);    }}

② 业务逻辑层实现

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.example.demo.mapper.UserMapper;import com.example.demo.model.User;import com.example.demo.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class UserServiceImpl extends ServiceImpl        implements UserService {    @Autowired    private UserMapper userMapper;    public boolean saveBatchCustom(List list){        return userMapper.saveBatchCustom(list);    }}

③ 数据持久层实现

import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.example.demo.model.User;import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapperpublic interface UserMapper extends BaseMapper{    boolean saveBatchCustom(List list);}

经过以上代码实现,我们就可以使用 MP 来实现数据的批量插入功能了,但本篇除了具体的实现代码之外,我们还要知道每种方法的执行效率,所以接下来我们来编写 MP 的测试代码。

MP 性能测试

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;@SpringBootTestclass UserControllerTest {    // 最大循环次数    private static final int MAXCOUNT = 100000;    @Autowired    private UserServiceImpl userService;    /**     * MP 批量插入     */    @Test    void saveBatch() {        long stime = System.currentTimeMillis(); // 统计开始时间        List list = new ArrayList<>();        for (int i = 0; i < MAXCOUNT; i++) {            User user = new User();            user.setName("test:" + i);            user.setPassword("123456");            list.add(user);        }        // MP 批量插入        userService.saveBatch(list);        long etime = System.currentTimeMillis(); // 统计结束时间        System.out.println("执行时间:" + (etime - stime));    }}

以上程序的执行总共花费了 6088 毫秒

从上述结果可知,使用 MP 的批量插入功能(插入数据 10W 条),它的性能比循环单次插入的性能提升了 14.5 倍。

MP 源码分析

从 MP 和循环单次插入的执行时间我们可以看出,使用 MP 并不是像有些朋友认为的那样,还是循环单次执行的,为了更清楚的说明此问题,我们查看了 MP 的源码。

MP 的核心实现代码是 saveBatch 方法,此方法的源码如下:

我们继续跟进 saveBatch 的重载方法:

从上述源码可以看出,MP 是将要执行的数据分成 N 份,每份 1000 条,每满 1000 条就会执行一次批量插入,所以它的性能要比循环单次插入的性能高很多。

那为什么要分批执行,而不是一次执行?别着急,当我们看了第 3 种实现方法之后我们就明白了。

3.原生批量插入

原生批量插入方法是依靠 MyBatis 中的 foreach 标签,将数据拼接成一条原生的 insert 语句一次性执行的,核心实现代码如下。

① 业务逻辑层扩展

在 UserServiceImpl 添加 saveBatchByNative 方法,实现代码如下:

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;import com.example.demo.mapper.UserMapper;import com.example.demo.model.User;import com.example.demo.service.UserService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class UserServiceImpl extends ServiceImpl        implements UserService {    @Autowired    private UserMapper userMapper;    public boolean saveBatchByNative(List list) {        return userMapper.saveBatchByNative(list);    }}

② 数据持久层扩展

在 UserMapper 添加 saveBatchByNative 方法,实现代码如下:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.example.demo.model.User;import org.apache.ibatis.annotations.Mapper;import java.util.List;@Mapperpublic interface UserMapper extends BaseMapper {    boolean saveBatchByNative(List list);}

③ 添加 UserMapper.xml

创建 UserMapper.xml 文件,使用 foreach 标签拼接 SQL,具体实现代码如下:

            INSERT INTO `USER`(`NAME`,`PASSWORD`) VALUES                    (#{item.name},#{item.password})            

经过以上步骤,我们原生的批量插入功能就实现的差不多了,接下来我们使用单元测试来查看一下此方法的执行效率。

原生批量插入性能测试

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;@SpringBootTestclass UserControllerTest {    // 最大循环次数    private static final int MAXCOUNT = 100000;    @Autowired    private UserServiceImpl userService;        /**     * 原生自己拼接 SQL,批量插入     */    @Test    void saveBatchByNative() {        long stime = System.currentTimeMillis(); // 统计开始时间        List 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));    }}

然而,当我们运行程序时却发生了以下情况:

纳尼?程序的执行竟然报错了。

缺点分析

从上述报错信息可以看出,当我们使用原生方法将 10W 条数据拼接成一个 SQL 执行时,由于拼接的 SQL 过大(4.56M)从而导致程序执行报错,因为默认情况下 MySQL 可以执行的最大 SQL(大小)为 4M,所以程序就报错了。

这就是原生批量插入方法的缺点,也是为什么 MP 需要分批执行的原因,就是为了防止程序在执行时,因为触发了数据库的最大执行 SQL 而导致程序执行报错。

解决方案

当然我们也可以通过设置 MySQL 的最大执行 SQL 来解决报错的问题,设置命令如下:

-- 设置最大执行 SQL 为 10Mset global max_allowed_packet=10*1024*1024;

"MyBatis批量插入数据的方法有哪些"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

数据 方法 循环 时间 测试 代码 性能 程序 最大 功能 统计 业务 源码 逻辑 接下来 数据库 核心 框架 分析 内容 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 东莞教育软件开发咨询 远程服务器工作时电脑可以关机吗 工业互联网公司德风科技 数据库系统软件开发 奇安信网络安全工程师认证有用吗 Web 服务器返回未知错误 蚌埠软件开发培训班 数据库创建补表 软件开发负责人的资质 服务器如何配置ipv6 阿里云服务器开启压缩 数据库设计用户表格 吉林幻影飞天网络技术有限公司 数据库应用技术教程微课版答案 服务器管理器打开的方法 redis淘汰前存储数据库 杭州掌玩网络技术公司 软件开发平台的特点 宁波网络技术开发信息中心 奉贤区企业软件开发信息中心 拓邦股份 软件开发 苹果id忘了重置连接服务器失败 服务器与交换机的连接距离 怎么要求软件开发票 怎么预防网络安全英语作文 福建企业软件开发商家 机关网络安全管理员总结 怎么映射一个服务器为一个盘 一起玩农场 服务器 民国图书数据库
0