最近在做一个批处理插入,需要对一个 List 中的数据通过批处理的方式插入到数据库当中去。但是目前发现List 数量过大导致单次拼接的SQL特别大,结果是拼接了SQL执行了大半天也没有执行完成。因此考虑到使用subList来给这个大 List 做个拆分,于是有了下面的代码。
public static void main(String[] args) {
int limit = 10;
for (int j = 0; j < 10000; j++) {
// 测试生成随机的 List 的 size
int size = new Random().nextInt(1000000000);
int count = (int) Math.ceil(size / (double) limit);
// 校验最后一批数据是否在 list 的序号区间中
int i = count - 1;
final int from = i * limit;
final int to = Math.min(size, (i + 1) * limit);
System.out.printf("current form : %d , to %d . %n", from, to);
// 校验是否能够符合逻辑
Assert.assertTrue(from<to);
System.out.printf("size : %d.%n", size);
}
}
这里的代码主要是用来做测试使用的,技术难点在于如何设置分批,然后循环。
// 根据 List 的 size 和 每批的数量获取总共需要执行的批次
int count = (int) Math.ceil(size / (double) limit);
这里是通过’总数/每批的数量’来获取需要执行的批次,但如上面所示,当两个整数相除时,得到的是没有余数的上。这样会导致一个问题,也即我们计算得到的批次可能会比我们实际要执行的批次少一次。
如当我们共有 55 条数据,每批为 10 条时,我们如果直接用 ‘总数/每批的数量’,取得执行批次为5;当我们同样有50条数据,每批为10条时,同样取得批次为5,这样的话就会少执行一批数据。如下面代码所示:
public static void main(String[] args) {
int limit = 10;
int size = 55;
int batch = size/limit;
for(int i = 0;i < batch;i++){
// 这样编写代码会发现少执行一批数据,
//当为<=时则又会在数量为整除时多执行一批数据从而导致
// IndexOutBoundException
System.out.printf("from index %d to index %d%n",i*limit,(i+1)*limit);
}
}
通过转换成浮点数计算的方式可以在由于余数的时候通过向上取整的方式多执行一批,然后通过下面代码来避免数组越界问题,即可完美解决分批问题。
final int to = Math.min(size, (i + 1) * limit);
完整的分批逻辑如下:
public static void main(String[] args) {
int limit = 10;
// 测试生成随机的 List 的 size
int size = 55;
int count = (int) Math.ceil(size / (double) limit);
List<String> list = new ArrayList<>(size);
for(int i = 0;i < count;i++){
final int from = i * limit;
final int to = Math.min(size, (i + 1) * limit);
System.out.printf("current form : %d , to %d . %n", from, to);
List<String> subList = list.subList(from,to);
// do your batch code
}
}