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