SpringBoot整合mybatisPlus實(shí)現(xiàn)批量插入并獲取ID詳解
背景:需要實(shí)現(xiàn)批量插入并且得到插入后的ID。
使用for循環(huán)進(jìn)行insert這里就不說(shuō)了,在海量數(shù)據(jù)下其性能是最慢的。數(shù)據(jù)量小的情況下,沒(méi)什么區(qū)別。
【1】saveBatch(一萬(wàn)條數(shù)據(jù)總耗時(shí):2478ms)
mybatisplus擴(kuò)展包提供的:com.baomidou.mybatisplus.extension.service.IService#saveBatch(java.util.Collection<T>)
測(cè)試代碼:
@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(一萬(wàn)條數(shù)據(jù)總耗時(shí):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>
測(cè)試代碼:
@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);
}
測(cè)試結(jié)果:

注意:這種方式得不到ID哦!
【3】MyBatis-Plus提供的InsertBatchSomeColumn方法(一萬(wàn)條數(shù)據(jù)總耗時(shí):690ms)
這里mybatisplus版本是3.3.0。
編寫MySqlInjector
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
//更新時(shí)自動(dòng)填充的字段,不用插入值
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}
為什么這里不用下面第二行的方式呢?
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE)); methodList.add(new InsertBatchSomeColumn());
這兩行代碼分別添加了兩個(gè) InsertBatchSomeColumn 方法到 methodList 中。
第一個(gè) InsertBatchSomeColumn 方法使用了一個(gè) Lambda 表達(dá)式作為參數(shù),該表達(dá)式用于過(guò)濾字段,只保留那些 getFieldFill 屬性不是 FieldFill.UPDATE 的字段。
第二個(gè) InsertBatchSomeColumn 方法沒(méi)有參數(shù),表示不進(jìn)行任何過(guò)濾,直接插入所有字段。
注入到配置類
@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);測(cè)試代碼:
@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);
}
測(cè)試結(jié)果

這里不僅實(shí)現(xiàn)了【2】的效果,還可以得到插入后的ID。
【4】假設(shè)一萬(wàn)條/十萬(wàn)條數(shù)據(jù)的情況下,執(zhí)行時(shí)間是多少
| 策略 | 一萬(wàn)條 | 十萬(wàn)條 |
|---|---|---|
| 方式一 | 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("一萬(wàn)條數(shù)據(jù)總耗時(shí):"+(end-start)+"ms");
}
一萬(wàn)條數(shù)據(jù)總耗時(shí):2478ms
十萬(wàn)條數(shù)據(jù)總耗時(shí):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("一萬(wàn)條數(shù)據(jù)總耗時(shí):"+(end-start)+"ms");
}
一萬(wàn)條數(shù)據(jù)總耗時(shí):474ms
十萬(wàn)條數(shù)據(jù)總耗時(shí):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("一萬(wàn)條數(shù)據(jù)總耗時(shí):"+(end-start)+"ms");
}
一萬(wàn)條數(shù)據(jù)總耗時(shí):690ms
十萬(wàn)條數(shù)據(jù)總耗時(shí):8339ms
【5】百萬(wàn)條數(shù)據(jù)的情況下進(jìn)行優(yōu)化
方式二、方式三都是拼接為一條SQL,也就說(shuō)有多少直接全部一次性插入,這就可能會(huì)導(dǎo)致最后的 sql 拼接語(yǔ)句特別長(zhǎng),超出了mysql 的限制。
這是什么意思呢?以MySQL為例,我們是需要考慮 max_allowed_packet 這個(gè)屬性配置大小。其決定了你最大可以單次發(fā)送包的大小,這里可以修改為64M也就是 67108864。
但是這個(gè)不是最優(yōu)解,最優(yōu)解應(yīng)該是控制每次插入的數(shù)量,比如一萬(wàn)條插入一次。
@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; // 計(jì)算總批次數(shù)量,確保最后一個(gè)批次也能處理
//保存單批提交的數(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ù)庫(kù)連接時(shí),可以開啟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來(lái)實(shí)現(xiàn)(這兩種未測(cè)試)。
到此這篇關(guān)于SpringBoot整合mybatisPlus實(shí)現(xiàn)批量插入并獲取ID詳解的文章就介紹到這了,更多相關(guān)SpringBoot整合mybatisPlus插入ID內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式實(shí)現(xiàn)方法詳解
這篇文章主要介紹了Java中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了Java中綴表達(dá)式轉(zhuǎn)換成后綴表達(dá)式的相關(guān)算法原理與具體實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-03-03
Java中通過(guò)Class類獲取Class對(duì)象的方法詳解
這篇文章主要給大家介紹了關(guān)于Java中通過(guò)Class類獲取Class對(duì)象的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用java具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面跟著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-08-08
Java中this和super的區(qū)別及this能否調(diào)用到父類使用
這篇文章主要介紹了Java中this和super的區(qū)別及this能否調(diào)用到父類使用,this和super都是Java中常見(jiàn)的關(guān)鍵字,下文關(guān)于兩者區(qū)別介紹,需要的小伙伴可以參考一下2022-05-05
Java實(shí)現(xiàn)批量化操作Excel文件的示例代碼
在操作Excel的場(chǎng)景中,通常會(huì)有一些針對(duì)Excel的批量操作,這篇文章主要為大家詳細(xì)介紹了如何使用GcExcel實(shí)現(xiàn)批量化操作Excel,感興趣的可以了解一下2024-12-12
Java基礎(chǔ)學(xué)習(xí)之標(biāo)簽
在Java中,標(biāo)簽必須在循環(huán)之前使用, 一個(gè)循環(huán)之中嵌套另一個(gè)循環(huán)的開關(guān),從多重嵌套中continue或break,該文詳細(xì)介紹了標(biāo)簽的相關(guān)知識(shí),對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們還很有幫助,需要的朋友可以參考下2021-05-05
Springcloud eureka搭建高可用集群過(guò)程圖解
這篇文章主要介紹了Springcloud eureka搭建高可用集群過(guò)程圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
淺談spring使用策略模式實(shí)現(xiàn)多種場(chǎng)景登錄方式
本文主要介紹了spring使用策略模式實(shí)現(xiàn)多種場(chǎng)景登錄方式,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12

