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")
    @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)来实现批量插入的

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

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

public interface TestMapper extends BaseMapper<Test> {
}

改为

public interface TestMapper extends BaseMapperPlus<Test> {
}

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Js-WebAPIs-事件(二)

事件监听&#xff08;绑定&#xff09; 什么是事件&#xff1f; 事件是在编程时系统内发生的动作或者发生的事情 比如用户在网页上单击一个按钮 什么是事件监听&#xff1f; 就是让程序检测是否有事件产生&#xff0c;一旦有事件触发&#xff0c;就立即调用一个函数做出响…

1.19(232.用栈实现队列)

1.19(232.用栈实现队列) 在push数据的时候&#xff0c;只要数据放进输入栈就好&#xff0c;但在pop的时候&#xff0c;操作就复杂一些&#xff0c;输出栈如果为空&#xff0c;就把进栈数据全部导入进来&#xff08;注意是全部导入&#xff09;&#xff0c;再从出栈弹出数据&a…

牛客月赛86+cf(edu)好题

思路&#xff1a;前缀和双指针 代码&#xff1a; #include <bits/stdc.h> using namespace std; using i64 int64_t; int main() {cin.tie(nullptr)->sync_with_stdio(false);cout << fixed << setprecision(20);int t 1;for (int ti 0; ti < t; …

C语言之通过指针操作字符串

下面我们来学习通过指针来灵活操作字符串的方法 判断字符串的长度 我们用对指针的遍历来实现判断字符串的长度 #include <stdio.h> int str_length(const char*s) {int len 0;while(*s)len;return len; } int main() {char str[128];printf("请输入字符&#xff…

猫咪增肥发腮吃什么?适合猫咪增肥发腮的猫罐头推荐

冬天开始了&#xff0c;北方的小猫咪们有暖气还好过一些&#xff0c;南方的猫咪就只能依靠自己的抵抗力来过冬了。如果不囤点肉肉&#xff0c;怕冷的小猫咪要怎么过冬啊&#xff1f;有些猫咪无论怎么吃也吃不胖&#xff0c;真的很让铲屎官烦恼。 作为一个开宠物店6年了的铲屎官…

温故而知新:直方图均衡、直方图匹配的再次理解

☞ ░ 前往老猿Python博客 ░ https://blog.csdn.net/LaoYuanPython 引言 数字图像处理的知识在2年前学过一阵子&#xff0c;但没学完&#xff0c;后来基于各种原因就停滞了整整2年没有学习了&#xff0c;现在准备重新开始&#xff0c;为此把以前学习写的总结博文翻出来重新进…

初识VUE

文章目录 Vue是什么1.创建一个Vue实例2.插值表达式{{ }}3.Vue的响应式特性4.开发者工具的安装 Vue是什么 概念&#xff1a;Vue是一个用于构建用户界面的渐进式框架 ①构建用户界面&#xff1a;基于数据渲染出用户看到的界面 ②渐进式&#xff1a;循序渐进 ③ 框架&#xff1…

【每日一题】2809. 使数组和小于等于 x 的最少时间-2024.1.19

题目&#xff1a; 2809. 使数组和小于等于 x 的最少时间 给你两个长度相等下标从 0 开始的整数数组 nums1 和 nums2 。每一秒&#xff0c;对于所有下标 0 < i < nums1.length &#xff0c;nums1[i] 的值都增加 nums2[i] 。操作 完成后 &#xff0c;你可以进行如下操作&…

什么是区块链?

区块链 区块链 &#xff08;英语&#xff1a;blockchain&#xff09;是借由 密码学 与 共识机制 等技术建立&#xff0c;存储数据的 保证不可篡改和不可伪造的 分布式技术。 什么是区块 区块 就是将一批数据打包在一起&#xff0c;并且给打包出来的区块编号。第一个区块的编…

北斗导航 | 基于恒定虚警率算法的接收机自主完好性监测(附Matlab代码)

===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 基于恒定虚警率算法的接收机自主完好性监测 输入输出外部引用代码参考…

基于springboot+vue的旅游网站系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

Java Springboot SSE如何判断客户端能否正常接收消息

目录 背景解决方案思路代码代码解释 Java反射知识点补充 背景 当新建一个 emitter 对象的时候, 它的默认超时时间是 30s. SseEmitter emitter new SseEmitter(); 但是很多情况下, 默认30s的时间太短, 需要把 emitter 对象的超时时间设置成不超时, 也就是永久有效. private …

Proxifier海外動態IP代理工具使用教程

Proxifier是一款多平臺代理客戶端&#xff0c;能讓不支持代理伺服器的程式正常運行。它支持各種操作系統和代理協議&#xff0c;並允許自定義端口和應用程式代理設置。用戶可以將其與代理伺服器集成&#xff0c;從而最大程度釋放性能效果。 本文將對其進行全面的概述&#xff…

Docker 安装 MySQ

Docker 安装 MySQL MySQL 是世界上最受欢迎的开源数据库。凭借其可靠性、易用性和性能&#xff0c;MySQL 已成为 Web 应用程序的数据库优先选择。 1、查看可用的 MySQL 版本 访问 MySQL 镜像库地址&#xff1a;https://hub.docker.com/_/mysql?tabtags 。 可以通过 Sort b…

上海亚商投顾:沪指冲高回落 旅游板块全天强势

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 沪指昨日冲高回落&#xff0c;创业板指跌近1%&#xff0c;北证50指数跌超3%。旅游、零售板块全天强势&#xf…

Red Hat Enterprise Linux 7.9 安装图解

引导和开始安装 选择倒计时结束前&#xff0c;通过键盘上下键选择下图框选项&#xff0c;启动图形化安装过程。需要注意的不同主板默认或者自行配置的固件类型不一致&#xff0c;引导界面有所不同。也就是说使用UEFI和BIOS的安装引导界面是不同的&#xff0c;如图所示。若手动调…

29、WEB攻防——通用漏洞SQL注入增删改查盲注延迟布尔报错

文章目录 盲注增删改查 盲注 概念&#xff1a;在注入过程中&#xff0c;获取的数据不能回显至前端页面&#xff0c;此时我们需要利用一些方法进行判断或尝试&#xff0c;这个过程被称为盲注。 解决&#xff1a;常规的联合查询注入不行的情况。 分类&#xff1a; 基于布尔的SQ…

【51单片机系列】proteus中的LCD12864液晶屏

文章来源&#xff1a;《单片机C语言编程与Proteus仿真技术》。 点阵字符型LCD显示模块只能显示英文字符和简单的汉字&#xff0c;要想显示较为复杂的汉字或图形&#xff0c;就必须采用点阵图型LCD显示模块&#xff0c;比如12864点阵图型LCD显示模块。 文章目录 一、 LCD12864点…

【算法】串联所有单词的子串【滑动窗口】

题目 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子串。例如&#xff0c;如果 words ["ab","cd","ef"]&#xff0c; 那么 "abcd…

milkv-duo cvi-mmf 硬件加速 JPG 解码性能测试

前言 本文是基于 nihui 老师的 opencv-mobile 对其支持 milkv-duo cvi-mmf 硬件加速 JPG 解码的测试。 nihui 老师原文章如下&#xff1a;opencv-mobile 现已支持 milkv-duo cvi-mmf 硬件加速 JPG 解码 opencv-mobile 仓库地址如下&#xff1a;nihui/opencv-mobile: The minim…