mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法
一.背景
最近mybatis-plus框架的更新,讓我們基礎(chǔ)開(kāi)發(fā)中如虎添翼。其中基本的增刪改查,代碼生成器想必大家用著那叫一個(gè)爽。本人在使用中,也遇到一些坑。比如savebatch,saveorupdatebatch,看著這不是批量新增,批量新增或更新嘛,看著api進(jìn)行開(kāi)發(fā),感覺(jué)也太好用啦。開(kāi)發(fā)完一測(cè)試,速度跟蝸牛一樣,針對(duì)大數(shù)據(jù)量真是無(wú)法忍受。在控制臺(tái)上發(fā)現(xiàn),怎么名義上是批量插入,還是一條一條的進(jìn)行插入,難怪速度龜速。
二.解決辦法
查閱網(wǎng)上資料,大體有兩種解決方案:
(1).使用mybatis的xml,自己進(jìn)行sql語(yǔ)句編寫(xiě)。該方法一個(gè)缺點(diǎn)是如果表的字段較多,有個(gè)幾十個(gè)字段,寫(xiě)批量新增,批量新增修改的sql語(yǔ)句真是個(gè)噩夢(mèng)。
INSERT INTO t
(id, age)
VALUES
(3, 28),
(4, 29)
ON DUPLICATE KEY UPDATE
id = VALUES(id),
age = VALUES(age);(2)mybatis-plus 新添加了一個(gè)sql注入器,通過(guò)sql注入器可以實(shí)現(xiàn)批量新增,批量新增修改功能。一次注入,隨時(shí)使用,使用極其方便。缺點(diǎn)就是項(xiàng)目啟動(dòng)時(shí)候,會(huì)進(jìn)行sql注入器注冊(cè),稍微影響啟動(dòng)速度。
三.sql注入器實(shí)現(xiàn)批量更新,批量新增或更新功能
(1)自定義mapper接口,繼承BaseMapper,定義實(shí)現(xiàn)的方法。
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 根Mapper,給表Mapper繼承用的,可以自定義通用方法
* {@link BaseMapper}
* {@link com.baomidou.mybatisplus.extension.service.IService}
* {@link com.baomidou.mybatisplus.extension.service.impl.ServiceImpl}
*/
public interface RootMapper<T> extends BaseMapper<T> {
/**
* 自定義批量插入
* 如果要自動(dòng)填充,@Param(xx) xx參數(shù)名必須是 list/collection/array 3個(gè)的其中之一
*/
int insertBatch(@Param("list") List<T> list);
/**
* 自定義批量新增或更新
* 如果要自動(dòng)填充,@Param(xx) xx參數(shù)名必須是 list/collection/array 3個(gè)的其中之一
*/
int mysqlInsertOrUpdateBath(@Param("list") List<T> list);
}(2)批量插入、批量新增或更新具體方法實(shí)現(xiàn)
批量插入具體方法實(shí)現(xiàn)如下:
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
/**
* 批量插入方法實(shí)現(xiàn)
*/
@Slf4j
public class InsertBatchMethod extends AbstractMethod {
/**
* insert into user(id, name, age) values (1, "a", 17), (2, "b", 18);
<script>
insert into user(id, name, age) values
<foreach collection="list" item="item" index="index" open="(" separator="),(" close=")">
#{item.id}, #{item.name}, #{item.age}
</foreach>
</script>
*/
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>insert into %s %s values %s</script>";
final String fieldSql = prepareFieldSql(tableInfo);
final String valueSql = prepareValuesSql(tableInfo);
final String sqlResult = String.format(sql, tableInfo.getTableName(), fieldSql, valueSql);
log.debug("sqlResult----->{}", sqlResult);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
// 第三個(gè)參數(shù)必須和RootMapper的自定義方法名一致
return this.addInsertMappedStatement(mapperClass, modelClass, "insertBatch", sqlSource, new NoKeyGenerator(), null, null);
}
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
tableInfo.getFieldList().forEach(x -> {
fieldSql.append(x.getColumn()).append(",");
});
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareValuesSql(TableInfo tableInfo) {
final StringBuilder valueSql = new StringBuilder();
valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
valueSql.delete(valueSql.length() - 1, valueSql.length());
valueSql.append("</foreach>");
return valueSql.toString();
}
}批量插入或更新具體方法如下:
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.springframework.util.StringUtils;
public class MysqlInsertOrUpdateBath extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>insert into %s %s values %s ON DUPLICATE KEY UPDATE %s</script>";
final String tableName = tableInfo.getTableName();
final String filedSql = prepareFieldSql(tableInfo);
final String modelValuesSql = prepareModelValuesSql(tableInfo);
final String duplicateKeySql =prepareDuplicateKeySql(tableInfo);
final String sqlResult = String.format(sql, tableName, filedSql, modelValuesSql,duplicateKeySql);
//System.out.println("savaorupdatesqlsql="+sqlResult);
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sqlResult, modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "mysqlInsertOrUpdateBath", sqlSource, new NoKeyGenerator(), null, null);
}
/**
* 準(zhǔn)備ON DUPLICATE KEY UPDATE sql
* @param tableInfo
* @return
*/
private String prepareDuplicateKeySql(TableInfo tableInfo) {
final StringBuilder duplicateKeySql = new StringBuilder();
if(!StringUtils.isEmpty(tableInfo.getKeyColumn())) {
duplicateKeySql.append(tableInfo.getKeyColumn()).append("=values(").append(tableInfo.getKeyColumn()).append("),");
}
tableInfo.getFieldList().forEach(x -> {
duplicateKeySql.append(x.getColumn())
.append("=values(")
.append(x.getColumn())
.append("),");
});
duplicateKeySql.delete(duplicateKeySql.length() - 1, duplicateKeySql.length());
return duplicateKeySql.toString();
}
/**
* 準(zhǔn)備屬性名
* @param tableInfo
* @return
*/
private String prepareFieldSql(TableInfo tableInfo) {
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
tableInfo.getFieldList().forEach(x -> {
fieldSql.append(x.getColumn()).append(",");
});
fieldSql.delete(fieldSql.length() - 1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
return fieldSql.toString();
}
private String prepareModelValuesSql(TableInfo tableInfo){
final StringBuilder valueSql = new StringBuilder();
valueSql.append("<foreach collection=\"list\" item=\"item\" index=\"index\" open=\"(\" separator=\"),(\" close=\")\">");
if(!StringUtils.isEmpty(tableInfo.getKeyProperty())) {
valueSql.append("#{item.").append(tableInfo.getKeyProperty()).append("},");
}
tableInfo.getFieldList().forEach(x -> valueSql.append("#{item.").append(x.getProperty()).append("},"));
valueSql.delete(valueSql.length() - 1, valueSql.length());
valueSql.append("</foreach>");
return valueSql.toString();
}
}(3)sql注入器實(shí)現(xiàn)
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import java.util.List;
/**
* 自定義方法SQL注入器
*/
public class CustomizedSqlInjector extends DefaultSqlInjector {
/**
* 如果只需增加方法,保留mybatis plus自帶方法,
* 可以先獲取super.getMethodList(),再添加add
*/
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new InsertBatchMethod());
methodList.add(new UpdateBatchMethod());
methodList.add(new MysqlInsertOrUpdateBath());
return methodList;
}
}(4)在自己想使用的mapper上繼承自定義的mapper.
import com.sy.adp.common.mybatisPlusExtend.RootMapper;
import com.sy.adp.flowfull.entity.InfMpmPds;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Component;
@Component
public interface InfMpmPdsMapper extends RootMapper<InfMpmPds> {
IPage<InfMpmPds> selectPageList(Page page, @Param("infMpmPds") InfMpmPds infMpmPds);
}(5)在controller或serviceImpi中引入mapper,使用自定義的方法。
>>>>>>>>>>>>>>>>>>>>引入mapper>>>>>>>>>>>>>>>

>>>>>>>>>>>>>>>>>>>>>>>>>方法使用>>>>>>>>>>>>

完成上述步驟,就可以運(yùn)行項(xiàng)目進(jìn)行測(cè)試看看,數(shù)據(jù)提升是不是幾個(gè)數(shù)量級(jí)。
到此這篇關(guān)于mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法的文章就介紹到這了,更多相關(guān)mybatis-plus savebatch,saveorupdatebatch內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springboot啟動(dòng)時(shí)運(yùn)行代碼詳解
在本篇內(nèi)容中我們給大家整理了關(guān)于在springboot啟動(dòng)時(shí)運(yùn)行代碼的詳細(xì)圖文步驟以及需要注意的地方講解,有興趣的朋友們學(xué)習(xí)下。2019-06-06
SpringBoot如何獲取當(dāng)前操作用戶(hù)的id/信息
在一般性的基設(shè)需求中,有需要獲取當(dāng)前用戶(hù)操作記錄的情況,也就是說(shuō)我們需要記錄當(dāng)前用戶(hù)的信息,如:id、昵稱(chēng)、賬號(hào)等信息,這篇文章主要介紹了SpringBoot獲取當(dāng)前操作用戶(hù)的id/信息,需要的朋友可以參考下2023-10-10
Spring?Boot配置文件的語(yǔ)法規(guī)則詳解(properties和yml)
這篇文章主要介紹了Spring?Boot配置文件的語(yǔ)法規(guī)則,主要介紹兩種配置文件的語(yǔ)法和格式,properties和yml,對(duì)于配置文件也有獨(dú)立的文件夾存放,主要用來(lái)存放一些需要經(jīng)過(guò)變動(dòng)的數(shù)據(jù)(變量值),感興趣的朋友跟隨小編一起看看吧2024-07-07
解讀maven項(xiàng)目啟動(dòng)tomcat不報(bào)錯(cuò)但是啟動(dòng)不起來(lái),tomcat啟動(dòng)到警告log4j就停止了
這篇文章主要介紹了maven項(xiàng)目啟動(dòng)tomcat不報(bào)錯(cuò)但是啟動(dòng)不起來(lái),tomcat啟動(dòng)到警告log4j就停止了問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Java 中HttpURLConnection附件上傳的實(shí)例詳解
這篇文章主要介紹了Java 中HttpURLConnection附件上傳的實(shí)例詳解的相關(guān)資料,希望通過(guò)本文大家能掌握這樣的知識(shí)內(nèi)容,需要的朋友可以參考下2017-09-09
Java實(shí)題演練二叉搜索樹(shù)與雙向鏈表分析
這篇文章主要介紹了Java二叉搜索樹(shù)與雙向鏈表,總的來(lái)說(shuō)這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達(dá)的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過(guò)程。希望通過(guò)這道題能給你帶來(lái)一種解題優(yōu)化的思路2022-12-12

