通用 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。」

相关推荐
消失的旧时光-19432 小时前
Kotlinx.serialization 对多态对象(sealed class )支持更好用
java·服务器·前端
xlq223222 小时前
15.list(上)
数据结构·c++·list
我不会插花弄玉2 小时前
排序【由浅入深-数据结构】
c语言·数据结构
leonardee3 小时前
Spring Security安全框架原理与实战
java·后端
q***5183 小时前
Spring Cloud gateway 路由规则
java
空空kkk3 小时前
SpringMVC框架——入门
java·spring
liyi_hz20084 小时前
云原生 + 国产化适配:O2OA (翱途)开发平台后端技术栈深度解析
java·后端·开源软件
XH华4 小时前
数据结构第三章:单链表的学习
数据结构
⑩-4 小时前
缓存穿透,击穿,雪崩
java·redis