SpringBoot整合mybatisPlus實(shí)現(xiàn)批量插入并獲取ID詳解
背景:需要實(shí)現(xiàn)批量插入并且得到插入后的ID。
使用for循環(huán)進(jìn)行insert這里就不說了,在海量數(shù)據(jù)下其性能是最慢的。數(shù)據(jù)量小的情況下,沒什么區(qū)別。
【1】saveBatch(一萬條數(shù)據(jù)總耗時:2478ms)
mybatisplus擴(kuò)展包提供的:com.baomidou.mybatisplus.extension.service.IService#saveBatch(java.util.Collection<T>)
測試代碼:
@Test
public void testBatch1(){
List<SysFile> list=new ArrayList<>();
list.add(new SysFile().setFileName("fiel1"));
list.add(new SysFile().setFileName("fiel2"));
list.add(new SysFile().setFileName("fiel3"));
list.add(new SysFile().setFileName("fiel4"));
list.add(new SysFile().setFileName("fiel5"));
list.add(new SysFile().setFileName("fiel6"));
fileService.saveBatch(list);
System.out.println(list);
}
我們分析其實(shí)現(xiàn)原理如下:com.baomidou.mybatisplus.extension.service.impl.ServiceImpl#saveBatch
@Transactional(rollbackFor = Exception.class)
@Override
public boolean saveBatch(Collection<T> entityList, int batchSize) {
String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
int size = entityList.size();
executeBatch(sqlSession -> {
int i = 1;
for (T entity : entityList) {
sqlSession.insert(sqlStatement, entity);
if ((i % batchSize == 0) || i == size) {
sqlSession.flushStatements();
}
i++;
}
});
return true;
}其實(shí)也就是一條條插入。

【2】集合方式foreach(一萬條數(shù)據(jù)總耗時:474ms)
SysFileMapper 自定義方法batchSaveFiles
public interface SysFileMapper extends BaseMapper<SysFile> {
int batchSaveFiles(List<SysFile> entityList);
}
xml實(shí)現(xiàn)
<insert id="batchSaveFiles">
insert into tb_sys_file (file_name) values
<foreach collection="list" item="item" separator=",">
(#{item.fileName})
</foreach>
</insert>
測試代碼:
@Test
public void testBatch2(){
List<SysFile> list=new ArrayList<>();
list.add(new SysFile().setFileName("fiel1"));
list.add(new SysFile().setFileName("fiel2"));
list.add(new SysFile().setFileName("fiel3"));
list.add(new SysFile().setFileName("fiel4"));
list.add(new SysFile().setFileName("fiel5"));
list.add(new SysFile().setFileName("fiel6"));
fileMapper.batchSaveFiles(list);
System.out.println(list);
}
測試結(jié)果:

注意:這種方式得不到ID哦!
【3】MyBatis-Plus提供的InsertBatchSomeColumn方法(一萬條數(shù)據(jù)總耗時:690ms)
這里mybatisplus版本是3.3.0。
編寫MySqlInjector
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
//更新時自動填充的字段,不用插入值
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}
為什么這里不用下面第二行的方式呢?
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE)); methodList.add(new InsertBatchSomeColumn());
這兩行代碼分別添加了兩個 InsertBatchSomeColumn 方法到 methodList 中。
第一個 InsertBatchSomeColumn 方法使用了一個 Lambda 表達(dá)式作為參數(shù),該表達(dá)式用于過濾字段,只保留那些 getFieldFill 屬性不是 FieldFill.UPDATE 的字段。
第二個 InsertBatchSomeColumn 方法沒有參數(shù),表示不進(jìn)行任何過濾,直接插入所有字段。
注入到配置類
@EnableTransactionManagement
@MapperScan({"com.enodeb.mapper"})
@Configuration
public class MybatisPlusConfig {
@Bean
public MySqlInjector sqlInjector() {
return new MySqlInjector();
}
}
SysFileMapper 自定義方法
public interface SysFileMapper extends BaseMapper<SysFile> {
int insertBatchSomeColumn(List<SysFile> entityList);測試代碼:
@Test
public void testBatch3(){
List<SysFile> list=new ArrayList<>();
list.add(new SysFile().setFileName("fiel1"));
list.add(new SysFile().setFileName("fiel2"));
list.add(new SysFile().setFileName("fiel3"));
list.add(new SysFile().setFileName("fiel4"));
list.add(new SysFile().setFileName("fiel5"));
list.add(new SysFile().setFileName("fiel6"));
fileMapper.insertBatchSomeColumn(list);
System.out.println(list);
}
測試結(jié)果

這里不僅實(shí)現(xiàn)了【2】的效果,還可以得到插入后的ID。
【4】假設(shè)一萬條/十萬條數(shù)據(jù)的情況下,執(zhí)行時間是多少
| 策略 | 一萬條 | 十萬條 |
|---|---|---|
| 方式一 | 2478ms | 20745ms |
| 方式二 | 474ms | 2904ms |
| 方式三 | 690ms | 8339ms |
① 方式一
@Test
public void testBatch1(){
long start=System.currentTimeMillis();
List<SysFile> list=new ArrayList<>();
SysFile sysFile;
for(int i=0;i<10000;i++){
sysFile=new SysFile();
sysFile.setFileName("file"+i);
list.add(sysFile);
}
fileService.saveBatch(list);
long end=System.currentTimeMillis();
System.out.println("一萬條數(shù)據(jù)總耗時:"+(end-start)+"ms");
}
一萬條數(shù)據(jù)總耗時:2478ms
十萬條數(shù)據(jù)總耗時:20745ms
② 方式二
@Test
public void testBatch2(){
long start=System.currentTimeMillis();
List<SysFile> list=new ArrayList<>();
SysFile sysFile;
for(int i=0;i<10000;i++){
sysFile=new SysFile();
sysFile.setFileName("file"+i);
list.add(sysFile);
}
fileMapper.batchSaveFiles(list);
long end=System.currentTimeMillis();
System.out.println("一萬條數(shù)據(jù)總耗時:"+(end-start)+"ms");
}
一萬條數(shù)據(jù)總耗時:474ms
十萬條數(shù)據(jù)總耗時:2904ms
③ 方式三
@Test
public void testBatch3(){
long start=System.currentTimeMillis();
List<SysFile> list=new ArrayList<>();
SysFile sysFile;
for(int i=0;i<10000;i++){
sysFile=new SysFile();
sysFile.setFileName("file"+i);
list.add(sysFile);
}
fileMapper.insertBatchSomeColumn(list);
long end=System.currentTimeMillis();
System.out.println("一萬條數(shù)據(jù)總耗時:"+(end-start)+"ms");
}
一萬條數(shù)據(jù)總耗時:690ms
十萬條數(shù)據(jù)總耗時:8339ms
【5】百萬條數(shù)據(jù)的情況下進(jìn)行優(yōu)化
方式二、方式三都是拼接為一條SQL,也就說有多少直接全部一次性插入,這就可能會導(dǎo)致最后的 sql 拼接語句特別長,超出了mysql 的限制。
這是什么意思呢?以MySQL為例,我們是需要考慮 max_allowed_packet 這個屬性配置大小。其決定了你最大可以單次發(fā)送包的大小,這里可以修改為64M也就是 67108864。
但是這個不是最優(yōu)解,最優(yōu)解應(yīng)該是控制每次插入的數(shù)量,比如一萬條插入一次。
@Test
public void testBatch4(){
List<SysFile> list=new ArrayList<>();
SysFile sysFile;
for(int i=0;i<100000;i++){
sysFile=new SysFile();
sysFile.setFileName("file"+i);
list.add(sysFile);
}
//設(shè)置每批次插入多少條數(shù)據(jù)
int batchSize=10000;
int count = (list.size() + batchSize - 1) / batchSize; // 計算總批次數(shù)量,確保最后一個批次也能處理
//保存單批提交的數(shù)據(jù)集合
List<SysFile> oneBatchList = new ArrayList<>(batchSize); // 預(yù)分配容量
??????? for (int i = 0; i < count; i++) {
int startIndex = i * batchSize;
int endIndex = Math.min(startIndex + batchSize, list.size());
oneBatchList.addAll(list.subList(startIndex, endIndex));
fileMapper.insertBatchSomeColumn(oneBatchList);
oneBatchList.clear(); // 清空集合以備下次循環(huán)使用
}
}【TIPS】
為了確保批量插入的高效性,還需要進(jìn)行一些配置和優(yōu)化。例如,在application.yml中配置數(shù)據(jù)庫連接時,可以開啟MySQL的批處理模式
【rewriteBatchedStatements=true】:
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/testBtach?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&rewriteBatchedStatements=true
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
此外還可以考慮使用jdbcTemplate.batchUpdate、Spring Batch來實(shí)現(xiàn)(這兩種未測試)。
到此這篇關(guān)于SpringBoot整合mybatisPlus實(shí)現(xiàn)批量插入并獲取ID詳解的文章就介紹到這了,更多相關(guān)SpringBoot整合mybatisPlus插入ID內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot使用Mybatis-plus分頁插件的案例詳解
這篇文章主要介紹了springboot使用Mybatis-plus分頁插件的相關(guān)知識,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05
Java 實(shí)戰(zhàn)范例之進(jìn)銷存管理系統(tǒng)的實(shí)現(xiàn)
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+vue+Springboot+ssm+mysql+maven+redis實(shí)現(xiàn)一個前后端分離的進(jìn)銷存管理系統(tǒng),大家可以在過程中查缺補(bǔ)漏,提升水平2021-11-11
Java二叉樹的遍歷思想及核心代碼實(shí)現(xiàn)
今天小編就為大家分享一篇關(guān)于Java二叉樹的遍歷思想及核心代碼實(shí)現(xiàn),小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-01-01
maven?springboot如何將jar包打包到指定目錄
這篇文章主要介紹了maven?springboot如何將jar包打包到指定目錄,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
SpringBoot利用Undertow實(shí)現(xiàn)高可用的反向代理配置
Undertow是一個采用Java開發(fā)的靈活的高性能Web服務(wù)器,本文將介紹如何利用?Undertow?服務(wù)器的反向代理能力,實(shí)現(xiàn)高可用的反向代理配置,感興趣的可以了解下2025-06-06
Springboot內(nèi)置的工具類之CollectionUtils示例講解
這篇文章主要介紹了Springboot內(nèi)置的工具類之CollectionUtils,本文通過示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-12-12
SpringCloud網(wǎng)關(guān)組件Gateway原理深度解析
Spring Cloud Gateway是Spring Cloud微服務(wù)生態(tài)下的網(wǎng)關(guān)組件,一些基礎(chǔ)的請求預(yù)處理的邏輯可以統(tǒng)一實(shí)現(xiàn)在網(wǎng)關(guān)這一層,這樣業(yè)務(wù)服務(wù)只需要專注于處理業(yè)務(wù)邏輯即可,所以本文就帶大家深度解析網(wǎng)關(guān)組件Gateway,需要的朋友可以參考下2023-07-07

