Mybatis批量处理

  1. Mybatis批量处理
    1. 批量插入
    2. 函数式使用方法

Mybatis批量处理

在实际开发中,我们可能会遇到有一批实体对象的数据需要一次性插入数据库,一般我们可以用最简单的for循环来对List类型的数据进行循环插入。

但是,每次插入都要链接一次数据库,执行一条sql语句,这样子在数据量少的情况下是可行的。

但是数据量有上万条呢? 不可能让mybatis循环插入一万次数据吧。

这里有两种解决方案

  • 第一种是使用mybatisPlus的batch插入功能,但是要引入MP框架。
  • 第二种是使用原生的sql插入。
insert into table(id,name,sex)
values
(1,"小明","male"),
(2,"小红","female"),
(3,"小华","male");

这种方法可以一条SQL语句插入多条数据

但是Mysql一般有默认SQL语句长度上限

这里我们需要注意数据量不要过大,因此我们需要对一万条数据进行拆分批量插入。

模仿MybatisPlus的方法将插入的数据量分为1000条数据一个批次,每次执行1000条数据的插入语句。

这样子即保证sql语句不会过长,也保证了批量插入的效率

批量插入

批次实体类,自动处理批次

默认将插入实体拆分为1000份为一批

package pers.os467.support.core;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

/**
 * 批次实体
 * @param <T> 批次内实例泛型
 */
public class Batch<T>{

    public static final int DEFAULT_BATCH_SIZE = 1000;

    //批列表
    private List<List<T>> batchList;

    //批数数量
    private Integer batchesCount;

    //每批容量
    private Integer batchSize = DEFAULT_BATCH_SIZE;

    //最后一批的容量
    private Integer lastBatchSize;

    public Batch(List<T> raw){
        init(raw);
    }

    public Batch(List<T> raw, Integer batchSize) {
        if (batchSize <= 0 ){
            throw new RuntimeException("batchSize must be positive");
        }
        this.batchSize = batchSize;
        init(raw);
    }

    private void init(List<T> raw) {
        int size = raw.size();
        if (size == 0){
            throw new RuntimeException("raw size is zero");
        }
        int var1 = size / batchSize;
        int var2 = size % batchSize;
        if (var2 == 0){
            batchesCount = var1;
            lastBatchSize = batchSize;
        }else {
            batchesCount = ++var1;
            lastBatchSize = var2;
        }
        batchList = new ArrayList<>();
        int k = 0;
        int var3 = batchesCount - 1;
        for (int i = 0; i < var3; i++) {
            batchList.add(new ArrayList<>());
            for (int j = 0; j < batchSize; j++) {
                batchList.get(i).add(raw.get(k++));
            }
        }
        List<T> batch = new ArrayList<>();
        for (int i = 0; i < lastBatchSize; i++) {
            batch.add(raw.get(k++));
        }
        batchList.add(batch);
    }

    public List<List<T>> getBatchList() {
        return batchList;
    }

    public void process(BatchProcess<T> batchProcess) {
        for (List<T> batch : batchList) {
            batchProcess.process(batch);
        }
    }
}

service中进行批次插入

这里是1000份为一批,来执行一条插入语句,如果份数过多会发生sql语句过长异常

但是效率比一条insert语句for循环要高很多

//将需要分组的数据交给Batch对象处理
Batch<FilePo> filePoBatch = new Batch<>(insertList);
//获取到批次列表
List<List<FilePo>> batches = filePoBatch.getBatches();
//批次批量插入
for (List<FilePo> batch : batches) {
    fileMapper.insertBatch(batch);
}

xml写法

<insert id="insertBatch" parameterType="java.util.List">
    INSERT INTO tb_file(
    c1
    ,c2
    ,c3
    ,c4
    ,c5
    ,c6
    ,c7
    ,c8
    )VALUES 
    <foreach collection="list" separator="," item="data">
        (
        #{data.p1}
        ,#{data.p2}
        ,#{data.p3}
        ,#{data.p4}
        ,#{data.p5}
        ,#{data.p6}
        ,#{data.p7}
        ,#{data.p8}
        )
    </foreach>
</insert>

函数式使用方法

这里使用@FunctionalInterface注解定义了一个函数接口

java8以上被此注解标注的接口方法允许使用匿名函数

@FunctionalInterface
public interface BatchProcess<T>{

    /**
     * 处理每一批的匿名函数
     * @param batch
     */
    void process(List<T> batch);

}

这里使用到了匿名函数的方法,对每批执行插入方法

//创建批处理对象
Batch<FilePo> filePoBatch = new Batch<>(insertList);
//处理每一批
filePoBatch.process(batch -> {
    fileMapper.insertBatch(batch);
});

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:Mybatis批量处理

字数:963

本文作者:Os467

发布时间:2024-01-28, 20:24:33

最后更新:2024-01-28, 20:25:27

原始链接:https://os467.github.io/2024/01/28/mybatis%E6%89%B9%E9%87%8F%E5%A4%84%E7%90%86/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏