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

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;

数据库的最终效果如下:

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

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;

@SpringBootTest

class 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 毫秒,如下图所示:

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

2.MP 批量插入

=========

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

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

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

com.baomidou

mybatis-plus-boot-starter

mybatis-plus-latest-version

注意:

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

更多 MP 框架的介绍请移步它的官网:

https://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;

@Service

public class UserServiceImpl extends ServiceImpl<UserMapper,User>

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;

@Mapper

public 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;

@SpringBootTest

class 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 毫秒,如下图所示:

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

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

MP 源码分析

=======

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

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

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

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

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

从上述源码可以看出,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;

@Service

public class UserServiceImpl extends ServiceImpl<UserMapper, User>

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;

@Mapper

public interface UserMapper extends BaseMapper {

boolean saveBatchByNative(List list);

}

③ 添加 UserMapper.xml

===================

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

<?xml version="1.0" encoding="UTF-8"?>

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;

@SpringBootTest

class UserControllerTest {

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值