背景分析
工作中,经常遇到需要用Java进行分页处理数据,例如把1000万条Excel数据写入MySQL数据库,如果把这1000w数据一股脑的丢给MySQL,保证把数据库玩完,故需要批量写入,如每批次写入500条。这时候就可以使用ListUtils.partition了。
maven坐标
commons-collections4和Guava两个jar包的坐标如下:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.0.1-jre</version>
</dependency>
批处理List
基于commons-collections4和Guava两个jar包,对java.util.List中海量数据进行分批处理的逻辑如下所示,请求参数都是传入List和每页处理的数据量:
public static void main(String[] args) {
List<String> list =new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("Wiener");
batchDealData(list, 3);
batchDealByGuava(list, 3);
}
private static void batchDealData (List data, int batchNum) {
// commons-collections4
List<List<String>> partitions = ListUtils.partition(data, batchNum);
partitions.stream().forEach(sublist -> {
System.out.println(sublist);
});
}
private static void batchDealByGuava (List data, int batchNum) {
// guava
List<List<String>> partitions = Lists.partition(data, batchNum);
partitions.stream().forEach(sublist -> {
System.out.println(sublist);
});
}
这种处理方法相对于手动分页,其优点显而易见,既可以降低代码复杂度,又可以提高开发效率。小编在《Java 使用线程池分批插入或者更新数据》中,介绍了一种通用分页方式,略显复杂,下面基于commons-collections4,优化其中的分页策略,代码如下:
private void batchDeal(List<Object> data, int batchNum) throws InterruptedException {
if (CollectionUtils.isEmpty(data)) {
return;
}
// 使用 ListUtils.partition分页
List<List<Object>> newList = ListUtils.partition(data, batchNum);
// 计算总页数
int pageNum = newList.size();
ExecutorService executor = Executors.newFixedThreadPool(pageNum);
try {
CountDownLatch countDownLatch = new CountDownLatch(pageNum);
for (int i = 0; i < pageNum; i++) {
ImportTask task = new ImportTask(newList.get(i), countDownLatch);
executor.execute(task);
}
countDownLatch.await();
log.info("数据操作完成!可以在此开始其它业务");
} finally {
// 关闭线程池,释放资源
executor.shutdown();
}
}
// 无改动
class ImportTask implements Runnable {
private List list;
private CountDownLatch countDownLatch;
public ImportTask(List data, CountDownLatch countDownLatch) {
this.list = data;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
if (null != list) {
// 业务逻辑,例如批量insert或者update
log.info("现在操作的数据是{}", list);
}
// 发出线程任务完成的信号
countDownLatch.countDown();
}
}