mybatis-plus批量保存异常及效率优化

最近基于自己公司内部服务维护,发现其中调度中心近期出现不少错误日志,但是该任务却是正常执行,生成的报表数据也是正常的,所以很多天没有发现问题

这就匪夷所思了,

   经仔细排查发现,是触发了feign超时hystrix熔断器机制

也就是说子服务出现了执行时间过长的情况

是什么让它花费这么多时间去执行呢,只有一个for循环,组装list<object>

这个组装过程在java看来是非常快,根本不可能出现问题

我发现了

 iXxxxService.saveBatch(xxxx);

mybatisplus3.3.2自带的批量保存的sql接口

跟踪代码的实现

在接口发现IService

   @Transactional(rollbackFor = {Exception.class})default boolean saveBatch(Collection<T> entityList) {return this.saveBatch(entityList, 1000);}boolean saveBatch(Collection<T> entityList, int batchSize);
ServiceImpl的实现
 @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();}}});}

可以看到这个是累计到一定数量一起 flush。

很多人认为这是mybatisplus设计的一个缺陷,是一条一条去做插入,其实这是错误,这种写法不仅没错还写的非常负责,具体接下来看

方法一

首先要结合数据库驱动来配合,大家注意这个 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,因为我们要改写它的批量插入语句,换成我们自己想要实现的方式

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")@Overridepublic 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);}@Overridepublic 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 {@Overridepublic List<AbstractMethod> getMethodList(Class<?> mapperClass) {List<AbstractMethod> methodList = super.getMethodList(mapperClass);//获取之前所有的方法methodList.add(new ZxsSaveBatch()); //添加自己的方法return methodList;}}

第四步

注入到spring

@Beanpublic ZxsSqlIntorPlus zxsSqlIntorPlus() {return new ZxsSqlIntorPlus();}

第五步

使用方法

之前我们是通过service.saveBatch(Xxxx)来实现批量插入的

这里我们需要改个地方,将原本的BaseMapper改成我们新创建的BaseMapperPlus

这是我的例子,当然,你也可以是其它的

public interface TestMapper extends BaseMapper<Test> {
}

改为

public interface TestMapper extends BaseMapperPlus<Test> {
}

然后在service里面通过baseMapper.zxsSaveBatch(Xxxx)

当然你也可以重写mybatisplus中IService的批量保存的

测一下速度,发现不用设置rewriteBatchedStatements,执行速度也更快了,几乎和rewriteBatchedStatements=true的速度相当

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/633555.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java常用API异常

1.包装类 1.1 基本类型包装类&#xff08;记忆&#xff09; 基本类型包装类的作用 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据 常用的操作之一&#xff1a;用于基本数据类型与字符串之间的转换 基本类型对应的包装类 基本数据类型包装类by…

司铭宇老师:房地产中介电话销售培训:房地产中介电话销售技巧

房地产中介电话销售培训&#xff1a;房地产中介电话销售技巧 在当今的房地产市场中&#xff0c;电话销售已经成为了房地产中介的重要营销手段。然而&#xff0c;如何才能有效地利用电话销售来吸引客户&#xff0c;提高销售业绩呢&#xff1f;这就需要掌握一些专业的电话销售技巧…

【React基础】– JSX语法

文章目录 认识JSX为什么React选择了JSXJSX的使用 React事件绑定this的绑定问题事件参数传递 React条件渲染React列表渲染列表中的key JSX的本质createElement源码Babel官网查看直接编写jsx代码 虚拟DOM的创建过程jsx – 虚拟DOM – 真实DOM声明式编程 阶段案例练习 认识JSX ◼ …

Elasticsearch(es)中must以及term的基本使用

文章目录 should 和 mustterm 和 range常见的查询方式 should 和 must 在 Elasticsearch&#xff08;ES&#xff09;中&#xff0c;should 和 must 是布尔查询&#xff08;Boolean Query&#xff09;中常用的两个子句。 should 子句&#xff1a;should 表示一个或多个条件之一满…

10个常考的前端手写题,你全都会吗?

前言 &#x1f4eb; 大家好&#xff0c;我是南木元元&#xff0c;热爱技术和分享&#xff0c;欢迎大家交流&#xff0c;一起学习进步&#xff01; &#x1f345; 个人主页&#xff1a;南木元元 今天来分享一下10个常见的JavaScript手写功能。 目录 1.实现new 2.call、apply、…

AliyunECS服务器在安装docker服务时一直出现:错误:为仓库 ‘root_n‘ 下载元数据失败

AliyunECS服务器在安装docker服务时一直出现:错误&#xff1a;为仓库 root_n 下载元数据失败 一、AliyunECS服务器在安装docker服务时一直出现:错误&#xff1a;为仓库 root_n 下载元数据失败endl 一、AliyunECS服务器在安装docker服务时一直出现:错误&#xff1a;为仓库 ‘roo…

Python爬虫---scrapy框架---当当网管道封装

项目结构&#xff1a; dang.py文件&#xff1a;自己创建&#xff0c;实现爬虫核心功能的文件 import scrapy from scrapy_dangdang_20240113.items import ScrapyDangdang20240113Itemclass DangSpider(scrapy.Spider):name "dang" # 名字# 如果是多页下载的话, …

transbigdata笔记:轨迹切片

1 方法介绍 在transbigdata笔记&#xff1a;轨迹停止点和行程提取-CSDN博客中&#xff0c;已经可以把轨迹点拆分成停止点和行程点&#xff0c;但是行程点只有起止位置&#xff0c;不包含行程轨迹信息为了进一步分析车辆的行驶轨迹&#xff0c;需要从每次行程的时间段中提取轨迹…

matlab Robotics Toolbox

工具箱下载地址&#xff1a;ROBOTICS TOOLBOX 安装教程 笛卡尔运动规划 在MATLAB中&#xff0c;ctraj和jtraj是两个不同的函数&#xff0c;主要用于控制系统和机器人学中的轨迹规划。它们之间的主要区别在于它们处理的问题类型和使用方式。 ctraj&#xff08;连续时间轨迹规划…

一文解读ISO26262安全标准:术语

一文解读ISO26262安全标准&#xff1a;术语 做汽车行业的人&#xff0c;都知道安全标准ISO26262&#xff0c;但是仔细说说它到底讲的是什么&#xff1f;好像又说不出来&#xff0c;这是个玄之又玄的话题&#xff0c;笔者试图将这份标准以简明扼要、并且容易理解的形式梳理出来&…

Milvus 上新:支持上万个 Collection、新增 Accesslog 功能……

2024 年开年&#xff0c;Milvus 好消息不断。除了收获 Github 25,000 颗星的成就&#xff0c;Milvus 也发布了新年的第一个版本—— 2.3.4。 此次发版的主要目的是为用户提供一个高扩展性且更易用的 Milvus 版本。为此&#xff0c;Milvus 新增了许多易用性功能&#xff0c;比如…

CTF CRYPTO 密码学-2

题目名称&#xff1a;crypto1 题目描述&#xff1a; 字符 ZZZZ X XXZ ZZ ZXZ Z ZXZ ZX ZZX XXX XZXX XXZ ZX ZXZZ ZZXZ XX ZX ZZ 分析 此字段是由Z和X组成的字符&#xff0c;联想到莫斯密码是由.和-组成的所以接下来可以尝试莫斯密码解题 解题过程&#xff1a; Step1&#xf…

c++ 指针的初始化

为指针正确赋值可以避免未定义行为&#xff08;如野指针&#xff09;。指针可以指向变量、数组、另一个指针&#xff08;多级指针&#xff09;、函数等。 指针的初始化可以在声明时进行&#xff0c;也可以在声明之后进行。 初始化为 nullptr 从 C11 开始&#xff0c;建议初始…

财务涉及到的金额之间的关系

财务涉及到的金额之间的关系 无税金额 含税金额 / (1 税率)含税金额 无税金额 * (1 税率)税额 无税金额 * 税率 含税金额 / (1 税率) 税率税率{0.03、0.13…}

2019年认证杯SPSSPRO杯数学建模C题(第二阶段)保险业的数字化变革全过程文档及程序

2019年认证杯SPSSPRO杯数学建模 基于统计建模的车险业数字变革研究 C题 保险业的数字化变革 原题再现&#xff1a; 车险&#xff0c;即机动车辆保险。保险自身是一种分散风险、消化损失的经济补偿制度&#xff0c;车险即为分散机动车辆在行驶过程中可能发作的未知风险和损失…

【Vue】Vue 路由的配置及使用

目录捏 前言一、路由是什么&#xff1f;1.前端路由2.后端路由 二、路由配置1.安装路由2.配置路由 三、路由使用1.route 与 router2. 声明式导航3. 指定组件的呈现位置 四、嵌套路由&#xff08;多级路由&#xff09;五、路由重定向1.什么是路由重定向&#xff1f;2.设置 redire…

奇安信天擎 rptsvr 任意文件上传漏洞复现

0x01 产品简介 奇安信天擎是奇安信集团旗下一款致力于一体化终端安全解决方案的终端安全管理系统(简称“天擎”)产品。通过“体系化防御、数字化运营”方法,帮助政企客户准确识别、保护和监管终端,并确保这些终端在任何时候都能可信、安全、合规地访问数据和业务。天擎基于…

C++类与对象【友元】

&#x1f308;个人主页&#xff1a;godspeed_lucip &#x1f525; 系列专栏&#xff1a;C从基础到进阶 &#x1f384;1 友元&#x1f951;1.1 全局函数做友元&#x1f951;1.2 类做友元&#x1f951;1.3 成员函数做友元 &#x1f56e;2 总结 &#x1f384;1 友元 生活中你的家…

ContentNegotiationManagerFactoryBean 内容协商

一.什么是内容协商 简单点说&#xff0c;就是同一资源,可以有多种表现形式&#xff0c;比如xml、json等&#xff0c;具体使用哪种表现形式&#xff0c;是可以协商的。 这是RESTfull的一个重要特性&#xff0c;Spring Web MVC也支持这个功能。 1.Spring MVC REST是如何决定采用…

深入了解性能优化(web应用)

影响一个系统性能的方方面面 一个 web应用不是一个孤立的个体,它是一个系统的部分,系统中的每一部分都会影响整个系统的性能 一.常用的性能评价/测试指标 1.响应时间 提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间。 常用操作的响应时间列表: 操作 响应…