淺談mybatis-plus批量保存異常及效率優(yōu)化
最近基于自己公司內(nèi)部服務維護,發(fā)現(xiàn)其中調(diào)度中心近期出現(xiàn)不少錯誤日志,但是該任務卻是正常執(zhí)行,生成的報表數(shù)據(jù)也是正常的,所以很多天沒有發(fā)現(xiàn)問題
這就匪夷所思了,經(jīng)仔細排查發(fā)現(xiàn),是觸發(fā)了feign超時hystrix熔斷器機制
也就是說子服務出現(xiàn)了執(zhí)行時間過長的情況
是什么讓它花費這么多時間去執(zhí)行呢,只有一個for循環(huán),組裝list<object>
這個組裝過程在java看來是非常快,根本不可能出現(xiàn)問題
我發(fā)現(xiàn)了
iXxxxService.saveBatch(xxxx);
mybatisplus3.3.2自帶的批量保存的sql接口
跟蹤代碼的實現(xiàn)
在接口發(fā)現(xiàn)IService
@Transactional( rollbackFor = {Exception.class} ) default boolean saveBatch(Collection<T> entityList) { return this.saveBatch(entityList, 1000); } boolean saveBatch(Collection<T> entityList, int batchSize);
ServiceImpl的實現(xiàn)
@Transactional( rollbackFor = {Exception.class} ) public boolean saveBatch(Collection<T> entityList, int batchSize) { String sqlStatement = this.sqlStatement(SqlMethod.INSERT_ONE); return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> { sqlSession.insert(sqlStatement, entity); }); }
protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) { Assert.isFalse(batchSize < 1, "batchSize must not be less than one", new Object[0]); return !CollectionUtils.isEmpty(list) && this.executeBatch((sqlSession) -> { int size = list.size(); int i = 1; for(Iterator var6 = list.iterator(); var6.hasNext(); ++i) { E element = var6.next(); consumer.accept(sqlSession, element); if (i % batchSize == 0 || i == size) { sqlSession.flushStatements(); } } }); }
可以看到這個是累計到一定數(shù)量一起 flush。
很多人認為這是mybatisplus設計的一個缺陷,是一條一條去做插入,其實這是錯誤,這種寫法不僅沒錯還寫的非常負責,具體接下來看
方法一
首先要結合數(shù)據(jù)庫驅(qū)動來配合,大家注意這個 rewriteBatchedStatements 玩意,其實mybatisplus批量保存與這個的首肯有很大關系
沒有加它之前
這是沒加之前最好的成績
加了之后最差的成績
可以非常直觀的看出效率明顯提高了好幾倍,所以呢千萬別誤會mybatisplus這個設計,人家完全交給你自主控制,你非得說是它的問題這就不好了
方法二
相比上面方法一的就比較粗暴了
我直接拿過來重寫saveBatch,或者增加一個特殊的批量保存
第一步
繼承mybatisplus自帶的BaseMapper(這里為什么要繼承我就不說了哈,懂的都懂),添加我們自定義的批量保存方法
zxsSaveBatch
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import java.util.Collection; public interface BaseMapperPlus <T> extends BaseMapper<T> { Integer zxsSaveBatch(Collection<T> entityList); }
第二步
繼承AbstractMethod,因為我們要改寫它的批量插入語句,換成我們自己想要實現(xiàn)的方式
import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.core.enums.SqlMethod; import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; import com.baomidou.mybatisplus.core.metadata.TableInfo; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; import lombok.AllArgsConstructor; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.experimental.Accessors; import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator; import org.apache.ibatis.executor.keygen.KeyGenerator; import org.apache.ibatis.executor.keygen.NoKeyGenerator; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlSource; import java.util.List; import java.util.function.Predicate; @NoArgsConstructor @AllArgsConstructor public class ZxsSaveBatch extends AbstractMethod { /** * 字段篩選條件 */ @Setter @Accessors(chain = true) private Predicate<TableFieldInfo> predicate; @SuppressWarnings("Duplicates") @Override public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) { KeyGenerator keyGenerator = new NoKeyGenerator(); SqlMethod sqlMethod = SqlMethod.INSERT_ONE; List<TableFieldInfo> fieldList = tableInfo.getFieldList(); String insertSqlColumn = tableInfo.getKeyInsertSqlColumn(false) + this.filterTableFieldInfo(fieldList, predicate, TableFieldInfo::getInsertSqlColumn, EMPTY); String columnScript = LEFT_BRACKET + insertSqlColumn.substring(0, insertSqlColumn.length() - 1) + RIGHT_BRACKET; String insertSqlProperty = tableInfo.getKeyInsertSqlProperty(ENTITY_DOT, false) + this.filterTableFieldInfo(fieldList, predicate, i -> i.getInsertSqlProperty(ENTITY_DOT), EMPTY); insertSqlProperty = LEFT_BRACKET + insertSqlProperty.substring(0, insertSqlProperty.length() - 1) + RIGHT_BRACKET; String valuesScript = SqlScriptUtils.convertForeach(insertSqlProperty, "list", null, ENTITY, COMMA); String keyProperty = null; String keyColumn = null; // 表包含主鍵處理邏輯,如果不包含主鍵當普通字段處理 if (tableInfo.havePK()) { if (tableInfo.getIdType() == IdType.AUTO) { /* 自增主鍵 */ keyGenerator = new Jdbc3KeyGenerator(); keyProperty = tableInfo.getKeyProperty(); keyColumn = tableInfo.getKeyColumn(); } else { if (null != tableInfo.getKeySequence()) { keyGenerator = TableInfoHelper.genKeyGenerator(getMethod(sqlMethod), tableInfo, builderAssistant); keyProperty = tableInfo.getKeyProperty(); keyColumn = tableInfo.getKeyColumn(); } } } String sql = String.format(sqlMethod.getSql(), tableInfo.getTableName(), columnScript, valuesScript); SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); return this.addInsertMappedStatement(mapperClass, modelClass, getMethod(sqlMethod), sqlSource, keyGenerator, keyProperty, keyColumn); } @Override public String getMethod(SqlMethod sqlMethod) { // 自定義 mapper 方法名 return "zxsSaveBatch"; } }
第三步
繼承DefaultSqlInjector,把我們的方法添加進去
import com.baomidou.mybatisplus.core.injector.AbstractMethod; import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; import java.util.List; public class ZxsSqlIntorPlus extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methodList = super.getMethodList(mapperClass);//獲取之前所有的方法 methodList.add(new ZxsSaveBatch()); //添加自己的方法 return methodList; } }
第四步
注入到spring
@Bean public ZxsSqlIntorPlus zxsSqlIntorPlus() { return new ZxsSqlIntorPlus(); }
第五步
使用方法
之前我們是通過service.saveBatch(Xxxx)來實現(xiàn)批量插入的
這里我們需要改個地方,將原本的BaseMapper改成我們新創(chuàng)建的BaseMapperPlus
這是我的例子,當然,你也可以是其它的
public interface TestMapper extends BaseMapper<Test> { }
改為
public interface TestMapper extends BaseMapperPlus<Test> { }
然后在service里面通過baseMapper.zxsSaveBatch(Xxxx)
當然你也可以重寫mybatisplus中IService的批量保存的
測一下速度,發(fā)現(xiàn)不用設置rewriteBatchedStatements,執(zhí)行速度也更快了,幾乎和rewriteBatchedStatements=true的速度相當
到此這篇關于淺談mybatis-plus批量保存異常及效率優(yōu)化的文章就介紹到這了,更多相關mybatis-plus批量保存異常內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java實現(xiàn)計網(wǎng)循環(huán)冗余檢驗算法的方法示例
這篇文章主要給大家介紹了關于Java實現(xiàn)計網(wǎng)循環(huán)冗余檢驗算法的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04Java實現(xiàn)動態(tài)獲取圖片驗證碼的示例代碼
這篇文章主要介紹了Java實現(xiàn)動態(tài)獲取圖片驗證碼的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-08-08關于springboot中nacos動態(tài)路由的配置
這篇文章主要介紹了springboot中nacos動態(tài)路由的配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09