Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.baomidou.mybatisplus.core.handlers;

import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

/**
* 批量结果处理器
*
* <p>将查询结果按指定批量大小缓存,
* 达到批量大小时调用批量消费函数进行批量处理,避免一次性加载全部数据导致内存压力过大
*
* <p>适用于导出Excel、批量写库、定时上报等需要批量操作场景,
* 能有效降低内存使用并提升处理性能
*
* <p><b>使用须知:</b><br>
* 1. 构造时传入非空的批量消费函数和合法的批量大小(>0)<br>
* 2. 结果回调时自动缓存并批量触发消费函数<br>
* 3. 查询完成后务必调用 {@link #flush()},处理剩余不足批量的数据,避免数据遗漏
*
* @param <T> 查询结果类型
* @author AprilWind
*/
public class BatchResultHandler<T> implements ResultHandler<T> {

/**
* 批量数据消费函数
*
* <p>注意:长时间的批量处理操作(如写Excel、写数据库或调用远程接口)可能导致数据库连接超时或断开,
* 以及MyBatis查询线程阻塞,影响系统性能和稳定性。
*
* <p>因此推荐:
* <ul>
* <li>结合合适的批量大小和限速策略,避免瞬时过载</li>
* <li>采用异步写入机制,将批量处理操作与MyBatis查询线程解耦,减少阻塞</li>
* <li>合理拆分查询任务,避免长时间持有数据库连接</li>
* </ul>
*
* <p>这样能有效保障系统的高性能和稳定运行
*/
private final Consumer<List<T>> batchConsumer;

/**
* 批量大小,必须大于0
*/
private final int batchSize;

/**
* 当前缓存数据
*/
private final List<T> buffer;

/**
* 构造批量结果处理器
*
* @param batchConsumer 批量消费函数,不能为null
* @param batchSize 批量大小,必须大于0
*/
public BatchResultHandler(Consumer<List<T>> batchConsumer, int batchSize) {
this.batchConsumer = batchConsumer;
this.batchSize = batchSize;
this.buffer = new ArrayList<>(batchSize);
}

/**
* MyBatis 查询结果回调,缓存单条结果,
* 达到批量大小时触发批量消费函数处理缓存数据
*
* @param context 查询结果上下文,包含单条结果
*/
@Override
public void handleResult(ResultContext<? extends T> context) {
buffer.add(context.getResultObject());
if (buffer.size() >= batchSize) {
flush();
}
}

/**
* 刷新缓存,触发批量消费函数处理剩余数据,
* 通常查询结束后调用,确保所有数据均被处理
*/
public void flush() {
if (!buffer.isEmpty()) {
batchConsumer.accept(new ArrayList<>(buffer));
buffer.clear();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.baomidou.mybatisplus.core.handlers;

import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

import java.util.function.Consumer;
import java.util.function.Predicate;

/**
* 过滤处理器
* <p>
* 通过传入的过滤条件({@link Predicate})筛选查询结果,
* 仅对满足条件的结果调用消费函数进行处理
* <p>
* 适用于需要对查询结果做条件过滤再处理的场景
* 避免无用数据进入后续处理逻辑,提高效率
*
* @param <T> 查询结果的数据类型
* @author AprilWind
*/
public class FilterResultHandler<T> implements ResultHandler<T> {

/**
* 过滤条件,用于判断是否处理该条结果
*/
private final Predicate<T> filter;

/**
* 处理满足过滤条件的结果的消费函数
*/
private final Consumer<T> consumer;

/**
* 构造过滤处理器
*
* @param filter 过滤条件
* @param consumer 满足过滤条件时的处理函数
*/
public FilterResultHandler(Predicate<T> filter, Consumer<T> consumer) {
this.filter = filter;
this.consumer = consumer;
}

/**
* 查询结果回调,先用过滤条件判断是否处理
* 满足条件则调用消费函数,否则忽略该条结果
*
* @param context 查询结果上下文,包含单条结果
*/
@Override
public void handleResult(ResultContext<? extends T> context) {
T result = context.getResultObject();
if (filter.test(result)) {
consumer.accept(result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.baomidou.mybatisplus.core.handlers;

import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;

import java.util.function.Consumer;

/**
* 单条处理处理器
*
* <p>适用于无需缓存或批量处理,直接对结果逐条消费的场景
* 逻辑简单直接,实时处理每条数据
*
* @param <T> 查询结果的数据类型
* @author AprilWind
*/
public class SimpleResultHandler<T> implements ResultHandler<T> {

/**
* 消费单条查询结果的函数
*/
private final Consumer<T> consumer;

/**
* 构造 SimpleResultHandler 实例
*
* @param consumer 处理单条查询结果的消费函数,不能为空
*/
public SimpleResultHandler(Consumer<T> consumer) {
this.consumer = consumer;
}

/**
* MyBatis 查询结果回调方法,
* 每接收到一条查询结果即调用消费函数进行处理
*
* @param context 查询结果上下文,包含当前单条结果对象
*/
@Override
public void handleResult(ResultContext<? extends T> context) {
T result = context.getResultObject();
consumer.accept(result);
}
}