淺談mybatis-plus批量保存異常及效率優(yōu)化
最近基于自己公司內(nèi)部服務(wù)維護(hù),發(fā)現(xiàn)其中調(diào)度中心近期出現(xiàn)不少錯(cuò)誤日志,但是該任務(wù)卻是正常執(zhí)行,生成的報(bào)表數(shù)據(jù)也是正常的,所以很多天沒有發(fā)現(xiàn)問題
這就匪夷所思了,經(jīng)仔細(xì)排查發(fā)現(xiàn),是觸發(fā)了feign超時(shí)hystrix熔斷器機(jī)制
也就是說子服務(wù)出現(xiàn)了執(zhí)行時(shí)間過長(zhǎng)的情況
是什么讓它花費(fèi)這么多時(shí)間去執(zhí)行呢,只有一個(gè)for循環(huán),組裝list<object>
這個(gè)組裝過程在java看來是非??欤静豢赡艹霈F(xiàn)問題
我發(fā)現(xiàn)了
iXxxxService.saveBatch(xxxx);
mybatisplus3.3.2自帶的批量保存的sql接口
跟蹤代碼的實(shí)現(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的實(shí)現(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();
}
}
});
}可以看到這個(gè)是累計(jì)到一定數(shù)量一起 flush。
很多人認(rèn)為這是mybatisplus設(shè)計(jì)的一個(gè)缺陷,是一條一條去做插入,其實(shí)這是錯(cuò)誤,這種寫法不僅沒錯(cuò)還寫的非常負(fù)責(zé),具體接下來看
方法一
首先要結(jié)合數(shù)據(jù)庫(kù)驅(qū)動(dòng)來配合,大家注意這個(gè) rewriteBatchedStatements 玩意,其實(shí)mybatisplus批量保存與這個(gè)的首肯有很大關(guān)系
沒有加它之前
這是沒加之前最好的成績(jī)

加了之后最差的成績(jī)

可以非常直觀的看出效率明顯提高了好幾倍,所以呢千萬(wàn)別誤會(huì)mybatisplus這個(gè)設(shè)計(jì),人家完全交給你自主控制,你非得說是它的問題這就不好了
方法二
相比上面方法一的就比較粗暴了
我直接拿過來重寫saveBatch,或者增加一個(gè)特殊的批量保存
第一步
繼承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,因?yàn)槲覀円膶懰呐坎迦胝Z(yǔ)句,換成我們自己想要實(shí)現(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;
// 表包含主鍵處理邏輯,如果不包含主鍵當(dāng)普通字段處理
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,把我們的方法添加進(jìn)去
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)來實(shí)現(xiàn)批量插入的
這里我們需要改個(gè)地方,將原本的BaseMapper改成我們新創(chuàng)建的BaseMapperPlus
這是我的例子,當(dāng)然,你也可以是其它的
public interface TestMapper extends BaseMapper<Test> {
}
改為
public interface TestMapper extends BaseMapperPlus<Test> {
}然后在service里面通過baseMapper.zxsSaveBatch(Xxxx)
當(dāng)然你也可以重寫mybatisplus中IService的批量保存的
測(cè)一下速度,發(fā)現(xiàn)不用設(shè)置rewriteBatchedStatements,執(zhí)行速度也更快了,幾乎和rewriteBatchedStatements=true的速度相當(dāng)
到此這篇關(guān)于淺談mybatis-plus批量保存異常及效率優(yōu)化的文章就介紹到這了,更多相關(guān)mybatis-plus批量保存異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java實(shí)現(xiàn)計(jì)網(wǎng)循環(huán)冗余檢驗(yàn)算法的方法示例
這篇文章主要給大家介紹了關(guān)于Java實(shí)現(xiàn)計(jì)網(wǎng)循環(huán)冗余檢驗(yàn)算法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
Java實(shí)現(xiàn)動(dòng)態(tài)獲取圖片驗(yàn)證碼的示例代碼
這篇文章主要介紹了Java實(shí)現(xiàn)動(dòng)態(tài)獲取圖片驗(yàn)證碼的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08
關(guān)于Android觸摸事件分發(fā)的原理詳析
觸摸事件分發(fā)機(jī)制一直以來都是Android中比較重要的一大塊,自定義view,各種復(fù)雜的自定義手勢(shì)交互都與觸摸事件分發(fā)機(jī)制關(guān)系密,下面這篇文章主要給大家介紹了關(guān)于Android觸摸事件分發(fā)原理的相關(guān)資料,需要的朋友可以參考下2022-01-01
IDEA 2021.2 激活教程及啟動(dòng)報(bào)錯(cuò)問題解決方法
這篇文章主要介紹了IDEA 2021.2 啟動(dòng)報(bào)錯(cuò)及激活教程,文章開頭給大家介紹了idea2021最新激活方法,關(guān)于idea2021啟動(dòng)報(bào)錯(cuò)的問題小編也給大家介紹的非常詳細(xì),需要的朋友可以參考下2021-10-10
聊聊注解@Aspect的AOP實(shí)現(xiàn)操作
這篇文章主要介紹了聊聊注解@Aspect的AOP實(shí)現(xiàn)操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2021-01-01
關(guān)于springboot中nacos動(dòng)態(tài)路由的配置
這篇文章主要介紹了springboot中nacos動(dòng)態(tài)路由的配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09

