mybatis-plus批量插入優(yōu)化方式
mybatis-plus批量插入優(yōu)化
背景
使用的mybatisplus的批量插入方法:
saveBatch(),打印 sql 日志發(fā)現(xiàn),底層還是一條條的 insert 語(yǔ)句,這顯然是不行的

優(yōu)化
之前就看到過(guò)網(wǎng)上都在說(shuō)在jdbc的url路徑上加上rewriteBatchedStatements=true 參數(shù)mysql底層才能開(kāi)啟真正的批量插入模式。但是我已經(jīng)添加了
通過(guò)查閱相關(guān)文檔后,發(fā)現(xiàn)mybatisPlus提供了sql注入器,我們可以自定義方法來(lái)滿足業(yè)務(wù)的實(shí)際開(kāi)發(fā)需求。
sql 注入器官網(wǎng):https://baomidou.com/guides/sql-injector/
mybatis-plus -core 核心包提供了基本的增刪查改注入器,在批量插入數(shù)據(jù)這里顯然不夠,所以可以看到在 mybaits-plus-extension 包下還額外提供了批量插入的可注入方法

AlwaysUpdateSomeColumnById: 根據(jù)Id更新每一個(gè)字段,全量更新不忽略null字段,解決mybatis-plus中updateById默認(rèn)會(huì)自動(dòng)忽略實(shí)體中null值字段不去更新的問(wèn)題;InsertBatchSomeColumn: 真實(shí)批量插入,通過(guò)單SQL的insert語(yǔ)句實(shí)現(xiàn)批量插入;Upsert:更新or插入,根據(jù)唯一約束判斷是執(zhí)行更新還是刪除,相當(dāng)于提供insert on duplicate key update支持。
我們只需要把這個(gè)方法添加進(jìn)我們的sql注入器即可。
config包新增如下兩個(gè)配置
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
//更新時(shí)自動(dòng)填充的字段,不用插入值
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}@Configuration
public class MybatisPlusConfig {
@Bean
public MySqlInjector sqlInjector() {
return new MySqlInjector();
}
}原先的 mapper 是這么寫的
public interface UserMapper extends BaseMapper<User> {
}我們新增了 InsertBatchSomeColumn 方法,需要重新定義一個(gè) BaseMapper
public interface CommonMapper<T> extends BaseMapper<T> {
/**
* 真正的批量插入
* @param entityList
* @return
*/
int insertBatchSomeColumn(List<T> entityList);
}public interface UserMapper extends CommonMapper<User> {
}優(yōu)化后的接口就對(duì)了,sql 顯示確實(shí)是 批量插入的語(yǔ)句

新的問(wèn)題
上面雖然實(shí)現(xiàn)了真正意義上的sql層面的批量插入。
但是,到這里并沒(méi)有結(jié)束,mybatisPlus官方提供的 insertBatchSomeColumn 方法不支持分批插入,也就是有多少直接全部一次性插入,這就可能會(huì)導(dǎo)致最后的 sql 拼接語(yǔ)句特別長(zhǎng),超出了mysql 的限制, 可能會(huì)報(bào)下面這個(gè)錯(cuò),

說(shuō)你這個(gè)包太大了??梢酝ㄟ^(guò)設(shè)置 max_allowed_packet 來(lái)改變包大小。
當(dāng)然我們可以通過(guò)下面的語(yǔ)句查詢當(dāng)前的配置大小:
select @@max_allowed_packet;
我這里就使用 sql 語(yǔ)句把值修改為 64M:
set global max_allowed_packet = 1024*1024*64;
但是改這個(gè)配置治標(biāo)不治本,能不能從代碼層面對(duì)拼接的 sql 語(yǔ)句做個(gè)優(yōu)化呢,限制不要太大,于是我們還要實(shí)現(xiàn)一個(gè)類似于saveBatch 分批的批量插入方法。
分批插入
模仿原來(lái)的saveBatch方法:
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Override
@Transactional(rollbackFor = {Exception.class})
public boolean saveBatch(Collection<User> entityList, int batchSize) {
try {
int size = entityList.size();
int idxLimit = Math.min(batchSize, size);
int i = 1;
//保存單批提交的數(shù)據(jù)集合
List<User> oneBatchList = new ArrayList<>();
for (Iterator<User> it = entityList.iterator(); it.hasNext(); ++i) {
User element = it.next();
oneBatchList.add(element);
if (i == idxLimit) {
baseMapper.insertBatchSomeColumn(oneBatchList);
//每次提交后需要清空集合數(shù)據(jù)
oneBatchList.clear();
idxLimit = Math.min(idxLimit + batchSize, size);
}
}
} catch (Exception e) {
log.error("saveBatch fail", e);
return false;
}
return true;
}
}從下面結(jié)果可以看到,最終的 sql 分成了兩個(gè)批次,這樣的話 sql 語(yǔ)句就不會(huì)太長(zhǎng)

springboot3整合mybaits-plus
這里就簡(jiǎn)單粘貼一下pom文件,注意 用mybatis-plus-spring-boot3-starter 這個(gè)依賴,不是用 mybatis-plus-spring-boot-starter ,不然報(bào)錯(cuò)
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.0</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example.springbootV3</groupId> <artifactId>springbootV3</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springbootV3</name> <properties> <java.version>17</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>3.1.3</version> </dependency> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> <version>3.5.5</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
spring.datasource.url=jdbc:mysql://192.168.133.128:3306/wxpay?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true&useSSL=false&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl mybatis-plus.mapper-locations=classpath:/com/example/demo/**/*Mapper.xml
mybatis 插入后返回主鍵
如果是使用了 mybatis-plus,可以直接使用封裝好的 insert 方法,通過(guò) service直接調(diào)用
userService.save(user); Integer id = user.getId();
如果直接使用 mybatis,有下面兩種方法。
- 一種是 在 insert 標(biāo)簽加入
useGeneratedKeys="true" keyProperty="id"屬性, - 一種是
selectKey標(biāo)簽
<?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="saveReturnPK1" parameterType="com.example.demo.entity.User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO `wxpay`.`t_user`(`name`, age) VALUES(#{name}, #{age})
</insert>
<insert id="saveReturnPK2" parameterType="com.example.demo.entity.User">
<selectKey keyProperty="id" resultType="int" order="AFTER">
SELECT LAST_INSERT_ID()
</selectKey>
INSERT INTO `wxpay`.`t_user`(`name`, age) VALUES(#{name}, #{age})
</insert>
</mapper>userMapper.saveReturnPK1(user); Integer id = user.getId();
mybaits-plus 代碼生成器
mybatis-plus新版本通過(guò) builder 模式可以快速生成你想要的代碼,快速且優(yōu)雅,官網(wǎng)在這里
public class CodeGenerator {
public static void main(String[] args) {
FastAutoGenerator.create("jdbc:mysql://192.168.133.128:3306/wxpay", "root", "root")
.globalConfig(builder -> {
builder.author("guang") // 設(shè)置作者
.enableSwagger() // 開(kāi)啟 swagger 模式
.outputDir("D://MP//"); // 指定輸出目錄
})
.dataSourceConfig(builder ->
builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {
int typeCode = metaInfo.getJdbcType().TYPE_CODE;
if (typeCode == Types.SMALLINT) {
// 自定義類型轉(zhuǎn)換
return DbColumnType.INTEGER;
}
return typeRegistry.getColumnType(metaInfo);
})
)
.packageConfig(builder ->
builder
.moduleName("com.example.demo") // 設(shè)置父包模塊名
.entity("entity")
.mapper("mapper")
.service("service")
.serviceImpl("service.impl")
.xml("mapper.xml")
.pathInfo(Collections.singletonMap(OutputFile.xml, "D://MP//")) // 設(shè)置mapperXml生成路徑
)
.strategyConfig(builder ->
builder.addInclude("t_user") // 設(shè)置需要生成的表名
.addTablePrefix("t_", "c_") // 設(shè)置過(guò)濾表前綴
.serviceBuilder().formatServiceFileName("%sService")
)
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默認(rèn)的是Velocity引擎模板
.execute();
}
}注意需要引入Freemarker 依賴,不然報(bào)錯(cuò)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency>
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
解決spring cloud gateway 獲取body內(nèi)容并修改的問(wèn)題
這篇文章主要介紹了解決spring cloud gateway 獲取body內(nèi)容并修改的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
Java CountDownLatch計(jì)數(shù)器與CyclicBarrier循環(huán)屏障
CountDownLatch是一種同步輔助,允許一個(gè)或多個(gè)線程等待其他線程中正在執(zhí)行的操作的ASET完成。它允許一組線程同時(shí)等待到達(dá)一個(gè)共同的障礙點(diǎn)2023-04-04
Spring MVC+MyBatis+MySQL實(shí)現(xiàn)分頁(yè)功能實(shí)例
分頁(yè)功能是我們?nèi)粘i_(kāi)發(fā)中經(jīng)常會(huì)遇到的,下面這篇文章主要給大家介紹了Spring MVC+MyBatis+MySQL實(shí)現(xiàn)分頁(yè)功能的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-06-06
Java?EasyExcel創(chuàng)建復(fù)雜表格的完整指南
WriteTable?是?EasyExcel?庫(kù)中的一個(gè)核心組件,用于在同一個(gè)?Excel?表單(Sheet)內(nèi)創(chuàng)建多個(gè)獨(dú)立的表格區(qū)域,下面我們來(lái)看看如何使用WriteTable創(chuàng)建復(fù)雜表格吧2025-12-12
Java讀取文件的簡(jiǎn)單實(shí)現(xiàn)方法
這篇文章主要介紹了Java讀取文件的簡(jiǎn)單實(shí)現(xiàn)方法,通過(guò)一個(gè)讀取txt格式的log文件為例,詳細(xì)的講述了Java讀取文件的方法及原理,需要的朋友可以參考下2014-09-09
SpringBoot項(xiàng)目中的多數(shù)據(jù)源支持的方法
本篇文章主要介紹了SpringBoot項(xiàng)目中的多數(shù)據(jù)源支持的方法,主要介紹在SpringBoot項(xiàng)目中利用SpringDataJpa技術(shù)如何支持多個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù)源,有興趣的可以了解一下2017-10-10
從零搭建SpringBoot+MyBatisPlus快速開(kāi)發(fā)腳手架
這篇文章主要為大家介紹了從零搭建SpringBoot+MyBatisPlus快速開(kāi)發(fā)腳手架示例教程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06

