mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法
一.背景
最近mybatis-plus框架的更新,讓我們基礎(chǔ)開發(fā)中如虎添翼。其中基本的增刪改查,代碼生成器想必大家用著那叫一個爽。本人在使用中,也遇到一些坑。比如savebatch,saveorupdatebatch,看著這不是批量新增,批量新增或更新嘛,看著api進(jìn)行開發(fā),感覺也太好用啦。開發(fā)完一測試,速度跟蝸牛一樣,針對大數(shù)據(jù)量真是無法忍受。在控制臺上發(fā)現(xiàn),怎么名義上是批量插入,還是一條一條的進(jìn)行插入,難怪速度龜速。
二.解決辦法
查閱網(wǎng)上資料,大體有兩種解決方案:
(1).使用mybatis的xml,自己進(jìn)行sql語句編寫。該方法一個缺點(diǎn)是如果表的字段較多,有個幾十個字段,寫批量新增,批量新增修改的sql語句真是個噩夢。
INSERT INTO t (id, age) VALUES (3, 28), (4, 29) ON DUPLICATE KEY UPDATE id = VALUES(id), age = VALUES(age);
(2)mybatis-plus 新添加了一個sql注入器,通過sql注入器可以實(shí)現(xiàn)批量新增,批量新增修改功能。一次注入,隨時使用,使用極其方便。缺點(diǎn)就是項(xiàng)目啟動時候,會進(jìn)行sql注入器注冊,稍微影響啟動速度。
三.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> { /** * 自定義批量插入 * 如果要自動填充,@Param(xx) xx參數(shù)名必須是 list/collection/array 3個的其中之一 */ int insertBatch(@Param("list") List<T> list); /** * 自定義批量新增或更新 * 如果要自動填充,@Param(xx) xx參數(shù)名必須是 list/collection/array 3個的其中之一 */ 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); // 第三個參數(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)行測試看看,數(shù)據(jù)提升是不是幾個數(shù)量級。
到此這篇關(guān)于mybatis-plus 關(guān)于savebatch,saveorupdatebatch遇到的坑及解決辦法的文章就介紹到這了,更多相關(guān)mybatis-plus savebatch,saveorupdatebatch內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot如何獲取當(dāng)前操作用戶的id/信息
在一般性的基設(shè)需求中,有需要獲取當(dāng)前用戶操作記錄的情況,也就是說我們需要記錄當(dāng)前用戶的信息,如:id、昵稱、賬號等信息,這篇文章主要介紹了SpringBoot獲取當(dāng)前操作用戶的id/信息,需要的朋友可以參考下2023-10-10Spring?Boot配置文件的語法規(guī)則詳解(properties和yml)
這篇文章主要介紹了Spring?Boot配置文件的語法規(guī)則,主要介紹兩種配置文件的語法和格式,properties和yml,對于配置文件也有獨(dú)立的文件夾存放,主要用來存放一些需要經(jīng)過變動的數(shù)據(jù)(變量值),感興趣的朋友跟隨小編一起看看吧2024-07-07解讀maven項(xiàng)目啟動tomcat不報(bào)錯但是啟動不起來,tomcat啟動到警告log4j就停止了
這篇文章主要介紹了maven項(xiàng)目啟動tomcat不報(bào)錯但是啟動不起來,tomcat啟動到警告log4j就停止了問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07Java 中HttpURLConnection附件上傳的實(shí)例詳解
這篇文章主要介紹了Java 中HttpURLConnection附件上傳的實(shí)例詳解的相關(guān)資料,希望通過本文大家能掌握這樣的知識內(nèi)容,需要的朋友可以參考下2017-09-09