最近 Review 小夥伴程式碼的時候,發現了一個小小的問題,小夥伴竟然在 for 迴圈中進行了 insert (插入)資料庫的操作,這就會導致每次迴圈時都會進行連線、插入、斷開連線的操作,從而導致一定的效能問題,簡化後代碼如下:

/**
* 插入操作
*/
@RequestMapping("/save")
public Object save() {
boolean flag = false; // 返回結果
// 待新增(使用者)資料
for (int i = 0; i < 1000; i++) {
User user = new User();
user.setName("test:"+i);
user.setPassword("123456");
// 插入資料
flag = userService.save(user);
if(!flag) break;
}
return flag;
}

這樣做並不會改變程式最終的執行結果,但會對程式的執行效率帶來很大的影響,就好比你現在要從 A 地點送 10 件貨到 B 地點,你可以選擇 1 次送 1 件,送 10 次的方案;也可以選擇 1 次送 10 件,送 1 次的方案,請問你會選擇哪種?這就是多次迴圈插入和批量一次插入的問題。

PS:要插入的資料量越大,批量插入的時間(相比於迴圈多次插入來說)也越短、其優勢也越大。

批量插入實現方案

本文我們使用 MyBatis-Plus(下文簡稱 MP)自帶的 saveBatch 方法,來實現資料的批量插入功能,因為 MP 不是本文討論的重點,所以這裡咱們就不介紹了,如果有不熟悉的朋友可以去他的官方自行惡補:https://baomidou.com/guide/,咱們本文重點介紹一下 MP 實現批量插入的具體步驟。

1.引入 MP 框架

首先,開啟您的 pom.xml 檔案,在檔案中新增以下內容:

<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>mybatis-plus-latest-version</version>
</dependency>

注意:mybatis-plus-latest-version 表示 MP 框架的最新版本號,可訪問 https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter 查詢最新版本號,但在使用的時候記得一定要將上面的 “mybatis-plus-latest-version”替換成換成具體的版本號,如 3.4.3 才能正常的引入框架。

2.建立資料庫和表

此步驟可省略,主要用於本文功能的實現,建立資料庫和資料表的指令碼如下:

-- ----------------------------
-- 建立資料庫
-- ----------------------------
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;

3.具體程式碼實現(重點)

① 實體類

先來建立資料庫所對應的 User 實體類:

import lombok.Getter;
import lombok.Setter; import java.util.Date; @Getter
@Setter
public class User {
private int id;
private String name;
private String password;
private Date createtime;
}

② Controller 層程式碼

本文的核心是使用 MP 框架中,IService 類提供的 saveBatch 方法,來實現批量資料的插入功能,對應在 Controller 中的實現程式碼如下:

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; /**
* MP 批量插入
*/
@RequestMapping("/savebatch")
public boolean saveBatch() {
List<User> 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.saveBatch(list);
}
}

③ Service 層程式碼(重點)

接下來,我們要建立一個 UserService 介面,繼承 MP 框架中的 IService 介面,實現程式碼如下:

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.model.User; public interface UserService extends IService<User> { }

然後再建立一個 UserService 的實現類:

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.stereotype.Service; @Service
public class UserServiceImpl extends ServiceImpl<UserMapper,User>
implements UserService { }

PS:注意 UserServiceImpl 必須要繼承 MP 框架中的 ServiceImpl,不然要重寫很多方法。

④ Mapper 層程式碼

Mapper 層的實現相對來說就比較簡單了,只需要建立一個 Mapper 類繼承 MP 框架中的 BaseMapper 類即可,實現程式碼如下:

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.model.User;
import org.apache.ibatis.annotations.Mapper; @Mapper
public interface UserMapper extends BaseMapper<User>{ }

PS:BaseMapper 提供了對某個物件(類)最基礎的 CRUD 操作。

總結

本文我們介紹了 MP(MyBatis Plus)中實現批量插入的具體實現步驟,它的核心是通過呼叫 MP 中 IService 提供的 saveBatch 方法來完成的,但如果專案中沒有引入 MP 框架該如何處理?是不是使用了 MP 就可以躺平了呢?不著急,下篇我們再聊批量插入的另一種方式(原生批量插入的實現方式),以及二者之間的優缺點分析。

關注公號「Java中文社群」檢視更多 MyBatis 和 Spring Boot 的系列文章。