首页 > 其他分享 >MybatisPlus批量插入探索

MybatisPlus批量插入探索

时间:2023-02-02 15:46:29浏览次数:32  
标签:MybatisPlus 批量 插入 baomidou import com mybatisplus

MybatisPlus批量插入探索

 最近在最近开发过程中遇到一个问题,在项目智慧物联网的大背景下,通常需要由多个传感器和监测设备向服务器请求,原设定是每次从mq中拉取1000数据批量插入到表分片中,在生产环境正式使用后发现最后一次成功插入时发现每次耗时需要18s,怎么会这么慢呢(—?—)赶紧多加几个分片[狗头]

JDBC配置参数rewriteBatchedStatements=true

MySQL的JDBC连接的url中要加rewriteBatchedStatements参数,并保证5.1.13以上版本的驱动,才能实现高性能的批量插入。
MySQL JDBC驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,批量插入实际上是单条插入,直接造成较低的性能。
只有把rewriteBatchedStatements参数置为true, 驱动才会帮你批量执行SQL
另外这个选项对INSERT/UPDATE/DELETE都有效

我的pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>shardingjdbcdemo</artifactId>
        <groupId>com.atguigu</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>mpkt</artifactId>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.3</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-spring-boot-starter</artifactId>
            <version>4.2.0</version>
        </dependency>

        <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>3.8.1</version>
        </dependency>

    </dependencies>


    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <!--指定资源的位置-->
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.yml</include>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
            </resource>
        </resources>
    </build>

</project>
View Code

我用的是saveBatch这个方法插入的

 jdbc后面加上参数后有明显的效率提升

查看源码发现在不加上rewriteBatchedStatements参数时其默认的会将sql拆分并一条条发给msql

 InsertBatchSomeColumn扩展插件

MybatisPlus在extension中提供了InsertBatchSomeColumn这个几个扩展方法。

AlwaysUpdateSomeColumnById: 根据Id更新每一个字段,全量更新不忽略null字段,解决mybatis-plus中updateById默认会自动忽略实体中null值字段不去更新的问题;
InsertBatchSomeColumn: 真实批量插入,通过单SQL的insert语句实现批量插入;
Upsert: 更新or插入,根据唯一约束判断是执行更新还是删除,相当于提供insert on duplicate key update支持。

 来看看官方的说法

 先不管它有没有bug我们先来试试看:

1、新建一个MySqlInjector文件继承DefaultSqlInjector将需要用的method添加进去就好了

package com.mpkt.config;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import com.baomidou.mybatisplus.extension.injector.methods.Upsert;
import org.springframework.stereotype.Component;

import java.util.List;

@Component
public class MySqlInjector  extends DefaultSqlInjector {

    @Override
    public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
        List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
        // 更新时自动填充的字段,不用插入值
        methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
        methodList.add(new AlwaysUpdateSomeColumnById());
        methodList.add(new Upsert());

        return methodList;
    }
}
View Code

2、新增一个名为SpiceBaseMapper的文件,把官方提供的注释copy下

3、userMapper改为继承这个新的文件就好啦

package com.mpkt.mapper;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import java.util.List;

public interface SpiceBaseMapper<T> extends BaseMapper<T> {

    /**
     * 批量插入
     * @param entityList
     * @return
     */
    int insertBatchSomeColumn(List<T> entityList);


}
View Code
package com.mpkt.mapper;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.mpkt.entity.User;
import org.apache.ibatis.annotations.Param;

public interface UserMapper extends SpiceBaseMapper<User> {

    /**
     * 分页查询
     * @param page
     * @return
     */
    Page<User> queryAll(@Param("page") Page<User> page);

    /**
     * 创建表
     * @param tableName
     */
    void createTable(@Param("tableName") String tableName);

}
View Code
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mpkt.mapper.UserMapper">

    <select id="queryAll" resultType="com.mpkt.entity.User">
        select * from user
    </select>

    <update id="createTable">
        CREATE TABLE ${tableName}  (
          `id` bigint(255) NOT NULL,
          `user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',
          PRIMARY KEY (`id`) USING BTREE
        ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

    </update>

</mapper>
View Code

嗯,又快了一点点,也没发现什么bug。

接下来说下rewriteBatchedStatements的作用,这个参数的意义是:在底层jdbc是通过测试用例2这样的批量处理的方式插入数据的时候,如rewriteBatchedStatements设为true,jdbc驱动就会把sql在本地客户端拼接成insert into table (a,b,c) values (x,x,x),(x,x,x)...这样的sql发送到服务端(这里注意,数据量很多的时候,并不会把所有数据拼接成一条sql,而是分批次拼接,因为mysql每次传输的sql大小是有限制的),否则这样的批量插入和一条一条数据的插入没有任何区别。

其实还可以自己通过代码的方式拼接出来(这里就不介绍了),至于用那种方式就需要自己衡量了。

多线程应用分批操作

 

标签:MybatisPlus,批量,插入,baomidou,import,com,mybatisplus
From: https://www.cnblogs.com/yiMro/p/17085124.html

相关文章

  • 【八大数据排序法】插入排序法的图形理解和案例实现 | C++
    第十六章插入排序法:::hljs-center目录第十六章插入排序法●前言●认识算法●一、插入排序法是什么?1.简要介绍2.图形理解3.算法分析●二、案例实现1.......
  • ES的BulkProcessor实现批量写入
    这是读取的文件0,联想(Lenovo)拯救者Y7000P英特尔酷睿i715.6英寸游戏笔记本电脑(8核i7-10875H16G512GRTX2060144Hz)灰,,7299.0,联想京东自营旗舰店,200000,电脑,10000,......
  • 冒泡排序+快速排序+插入排序(实现)
    王道督学营16/*Description读取10个整型数据1263589541356503844,然后通过冒泡排序,快速排序,插入排序,分别对该组数据进行排序,输出3次有序结果,每个数的输出占3个......
  • Python读取大量Excel文件并跨文件批量计算平均值
      本文介绍基于Python语言,实现对多个不同Excel文件进行数据读取与平均值计算的方法。  首先,让我们来看一下具体需求:目前有一个文件夹,其中存放了大量Excel文件;文件名称......
  • github - 批量删除仓库存储
    github-批量删除仓库存储github是不提供批量删除的,网上一堆写脚本的形式,让自动去一个一个删除,这里有个更好的办法,一位外国开发者将这个批量删除的功能做成了网站:​​https......
  • 【解决方案】多线程如何避免重复插入
    【先记录,日后完善验证】1、设置唯一键,如果是插入或更新操作可以增加trycatch,把更新的sql写在catch里面;2、synchornized关键字和lock接口,但是只能用于单机;4、多服务可......
  • 插入排序(Insertion Sort)
    一、算法概述1.1算法分类十种常见排序算法可以分为两大类:比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排......
  • openpyxl 插入图片
    fromioimportBytesIOfromopenpyxl.drawing.imageimportImage#BytesIO可以将数据存入内存,模拟文件的操作方式read或者writef=BytesIO()f.write(requests.get(u......
  • js插入样式表
    js插入样式表varstyle=document.createElement('style');style.innerHTML=`body{background-color:aquamarine!important;}`;//获取第一个脚本标记varscri......
  • SQL Server批量数据插入SQL语句
    1.INSERTINTOSELECT语句语句形式为:InsertintoTable2(field1,field2,…)selectvalue1,value2,…fromTable1要求目标表Table2必须存在,由于目标表Table2已经存在,所以......