通用 List 分批切割并循环查询数据库工具类

通用 List 分批切割并循环查询数据库工具类

有大量 ID / 参数需要查库时,直接 IN (...) 容易:

  • 超过数据库 / JDBC 支持的参数个数
  • 一次性加载太多数据,内存、网络压力大

所以做一个通用的"切 List + 分批查库"的工具类,只要传入原始 List、批大小和查询 lambda 即可。


记得点赞加收藏哦😁😁😁

一、通用工具类实现(ListBatchExecutor)

java 复制代码
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * List 分批处理工具类:
 * 1) splitAndQuery:分批查询并合并结果
 * 2) splitAndConsume:分批执行(无返回值),适合批量删除 / 更新
 */
public class ListBatchExecutor {

    /**
     * 分批执行查询,并把所有结果合并返回
     *
     * @param sourceList 原始参数列表(比如一堆 id)
     * @param batchSize  每批大小(比如 1000)
     * @param queryFunc  每批查询逻辑:入参是这一批的 List,返回这一批查出来的结果 List
     * @param <P>        入参类型(比如 Long id)
     * @param <R>        返回结果类型(比如 User)
     */
    public static <P, R> List<R> splitAndQuery(
            List<P> sourceList,
            int batchSize,
            Function<List<P>, List<R>> queryFunc) {

        if (sourceList == null || sourceList.isEmpty()) {
            return Collections.emptyList();
        }
        if (batchSize <= 0) {
            throw new IllegalArgumentException("batchSize 必须 > 0");
        }
        if (queryFunc == null) {
            throw new IllegalArgumentException("queryFunc 不能为空");
        }

        List<R> result = new ArrayList<>();
        int total = sourceList.size();

        for (int i = 0; i < total; i += batchSize) {
            int end = Math.min(i + batchSize, total);
            List<P> subList = sourceList.subList(i, end);

            // 执行这一批的查询
            List<R> batchResult = queryFunc.apply(subList);
            if (batchResult != null && !batchResult.isEmpty()) {
                result.addAll(batchResult);
            }
        }
        return result;
    }

    /**
     * 分批执行,不关心返回值的版本(比如批量更新、批量删除)
     *
     * @param sourceList 原始参数列表
     * @param batchSize  每批大小
     * @param consumer   每批消费逻辑
     * @param <P>        入参类型
     */
    public static <P> void splitAndConsume(
            List<P> sourceList,
            int batchSize,
            Consumer<List<P>> consumer) {

        if (sourceList == null || sourceList.isEmpty()) {
            return;
        }
        if (batchSize <= 0) {
            throw new IllegalArgumentException("batchSize 必须 > 0");
        }
        if (consumer == null) {
            throw new IllegalArgumentException("consumer 不能为空");
        }

        int total = sourceList.size();
        for (int i = 0; i < total; i += batchSize) {
            int end = Math.min(i + batchSize, total);
            List<P> subList = sourceList.subList(i, end);
            consumer.accept(subList);
        }
    }
}

二、在 Service / Mapper 中的使用示例

1. 使用 MyBatis-Plus 分批 IN 查询

java 复制代码
// 一堆 userId,需要分批查用户
List<Long> userIdList = ...;

List<User> userList = ListBatchExecutor.splitAndQuery(
        userIdList,
        1000,  // 每批 1000 个 id
        subIds -> userMapper.selectList(
                new LambdaQueryWrapper<User>()
                        .in(User::getId, subIds)
        )
);

2. 分批删除 / 更新(不需要返回值)

java 复制代码
List<Long> userIdList = ...;

// 分批删除用户
ListBatchExecutor.splitAndConsume(
        userIdList,
        500,
        subIds -> {
            userMapper.delete(
                    new LambdaQueryWrapper<User>()
                            .in(User::getId, subIds)
            );
        }
);

你也可以改成批量更新:

java 复制代码
ListBatchExecutor.splitAndConsume(
        userIdList,
        500,
        subIds -> {
            // 示例:把这些用户标记为禁用
            userMapper.disableUsersByIds(subIds);
        }
);

三、如果 Mapper 用的是 XML 写法

1. Mapper 接口

java 复制代码
public interface UserMapper {

    List<User> selectByIdList(@Param("idList") List<Long> idList);

    int deleteByIdList(@Param("idList") List<Long> idList);
}

2. Mapper XML

xml 复制代码
<select id="selectByIdList" resultType="com.xxx.User">
    SELECT *
    FROM t_user
    WHERE id IN
    <foreach collection="idList" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<delete id="deleteByIdList">
    DELETE FROM t_user
    WHERE id IN
    <foreach collection="idList" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

3. 调用方式

java 复制代码
// 分批查询
List<User> userList = ListBatchExecutor.splitAndQuery(
        userIdList,
        1000,
        subIds -> userMapper.selectByIdList(subIds)
);

// 分批删除
ListBatchExecutor.splitAndConsume(
        userIdList,
        500,
        subIds -> userMapper.deleteByIdList(subIds)
);

四、这个工具类能解决什么问题?

  • 防止单次 SQL 参数过多 :避免 IN (太长) 触发数据库 / 驱动限制。
  • 控制单次压力:每批条数可控,减轻单次 IO 和内存压力。
  • 完全通用:与具体表、Mapper 解耦,只依赖你传入的 lambda。
  • 场景广泛:批量查询、批量删除、批量更新、批量调第三方接口都能用。

你只要记住一句用法:

「有一堆 List,要分批处理,就丢给 ListBatchExecutor。」

相关推荐
yuniko-n14 分钟前
【JUC】Lock 和 synchronzied 锁
java
西柚小萌新20 分钟前
【LLM&&AI应用开发 八股文】--4.3.Agent智能体(下)
java·开发语言·数据库
北极有牛23 分钟前
cpp学习笔记--常量指针
java·开发语言·算法
java资料站30 分钟前
二、Spring AI Alibaba · ChatModel
java·windows·spring
十年Java程序媛1 小时前
Java 接口和抽象类对比|Java8 新特性,抛弃老旧八股,正确选型
java·spring boot·后端
caoerzhong1 小时前
JeeWMS 开源 WMS 部署避坑指南:Java 仓库管理系统的环境基线、四类根因与可复现交付
java·开发语言·开源
用户3721574261351 小时前
Java 合并 PDF 文件:完整合并、指定页面合并与流合并
java
for_ever_love__2 小时前
MySQL 全文索引实战:FULLTEXT、ngram 中文分词与 MATCH AGAINST 到底该怎么用
java·python·mysql·全文检索·分词·索引·ngram
java资料站2 小时前
五、Spring AI Alibaba · Memory · Saver(短期会话)
java·spring·microsoft
夜之眷属3 小时前
Core dump 崩溃排查:JVM 宕机后,那份 core 文件怎么用 gdb 还原现场
java·运维·服务器·jvm