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