From a58caadbaacaa04b38a3c14563addbe7e24b2ff5 Mon Sep 17 00:00:00 2001
From: AprilWind <2100166581@qq.com>
Date: Sun, 3 Aug 2025 15:42:16 +0800
Subject: [PATCH 1/2] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9EResultHandler?=
=?UTF-8?q?=E5=A4=84=E7=90=86=E5=99=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../mybatis/handler/BatchResultHandler.java | 101 ++++++++++++++++++
.../mybatis/handler/FilterResultHandler.java | 64 +++++++++++
.../mybatis/handler/SimpleResultHandler.java | 48 +++++++++
3 files changed, 213 insertions(+)
create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java
create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java
create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java
new file mode 100644
index 000000000..bd505a038
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java
@@ -0,0 +1,101 @@
+package org.dromara.common.mybatis.handler;
+
+import cn.hutool.core.collection.CollUtil;
+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;
+
+/**
+ * 批量结果处理器
+ *
+ *
将查询结果按指定批量大小缓存,
+ * 达到批量大小时调用批量消费函数进行批量处理,避免一次性加载全部数据导致内存压力过大
+ *
+ *
适用于导出Excel、批量写库、定时上报等需要批量操作场景,
+ * 能有效降低内存使用并提升处理性能
+ *
+ *
使用须知:
+ * 1. 构造时传入非空的批量消费函数和合法的批量大小(>0)
+ * 2. 结果回调时自动缓存并批量触发消费函数
+ * 3. 查询完成后务必调用 {@link #flush()},处理剩余不足批量的数据,避免数据遗漏
+ *
+ * @param 查询结果类型
+ * @author AprilWind
+ */
+public class BatchResultHandler implements ResultHandler {
+
+ /**
+ * 批量数据消费函数
+ *
+ * 注意:长时间的批量处理操作(如写Excel、写数据库或调用远程接口)可能导致数据库连接超时或断开,
+ * 以及MyBatis查询线程阻塞,影响系统性能和稳定性。
+ *
+ *
因此推荐:
+ *
+ * - 结合合适的批量大小和限速策略,避免瞬时过载
+ * - 采用异步写入机制,将批量处理操作与MyBatis查询线程解耦,减少阻塞
+ * - 合理拆分查询任务,避免长时间持有数据库连接
+ *
+ *
+ * 这样能有效保障系统的高性能和稳定运行
+ */
+ private final Consumer> batchConsumer;
+
+ /**
+ * 批量大小,必须大于0
+ */
+ private final int batchSize;
+
+ /**
+ * 当前缓存数据
+ */
+ private final List buffer;
+
+ /**
+ * 构造批量结果处理器
+ *
+ * @param batchConsumer 批量消费函数,不能为null
+ * @param batchSize 批量大小,必须大于0
+ * @throws NullPointerException batchConsumer为null时抛出
+ * @throws IllegalArgumentException batchSize小于等于0时抛出
+ */
+ public BatchResultHandler(Consumer> batchConsumer, int batchSize) {
+ if (batchConsumer == null) {
+ throw new NullPointerException("batchConsumer不能为null");
+ }
+ if (batchSize <= 0) {
+ throw new IllegalArgumentException("batchSize必须大于0");
+ }
+ 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 (CollUtil.isNotEmpty(buffer)) {
+ batchConsumer.accept(new ArrayList<>(buffer));
+ buffer.clear();
+ }
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java
new file mode 100644
index 000000000..6496458d6
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java
@@ -0,0 +1,64 @@
+package org.dromara.common.mybatis.handler;
+
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
+
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+
+/**
+ * 过滤处理器
+ *
+ * 通过传入的过滤条件({@link Predicate})筛选查询结果,
+ * 仅对满足条件的结果调用消费函数进行处理
+ *
+ * 适用于需要对查询结果做条件过滤再处理的场景
+ * 避免无用数据进入后续处理逻辑,提高效率
+ *
+ * @param 查询结果的数据类型
+ * @author AprilWind
+ */
+public class FilterResultHandler implements ResultHandler {
+
+ /**
+ * 过滤条件,用于判断是否处理该条结果
+ */
+ private final Predicate filter;
+
+ /**
+ * 处理满足过滤条件的结果的消费函数
+ */
+ private final Consumer consumer;
+
+ /**
+ * 构造过滤处理器
+ *
+ * @param filter 过滤条件
+ * @param consumer 满足过滤条件时的处理函数
+ * @throws NullPointerException 如果 filter 或 consumer 为 null
+ */
+ public FilterResultHandler(Predicate filter, Consumer consumer) {
+ if (filter == null) {
+ throw new NullPointerException("过滤条件,不能为空");
+ }
+ if (consumer == null) {
+ throw new NullPointerException("处理函数,不能为空");
+ }
+ 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);
+ }
+ }
+}
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java
new file mode 100644
index 000000000..c5950da1a
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java
@@ -0,0 +1,48 @@
+package org.dromara.common.mybatis.handler;
+
+import org.apache.ibatis.session.ResultContext;
+import org.apache.ibatis.session.ResultHandler;
+
+import java.util.function.Consumer;
+
+/**
+ * 单条处理处理器
+ *
+ * 适用于无需缓存或批量处理,直接对结果逐条消费的场景
+ * 逻辑简单直接,实时处理每条数据
+ *
+ * @param 查询结果的数据类型
+ * @author AprilWind
+ */
+public class SimpleResultHandler implements ResultHandler {
+
+ /**
+ * 消费单条查询结果的函数
+ */
+ private final Consumer consumer;
+
+ /**
+ * 构造 SimpleResultHandler 实例
+ *
+ * @param consumer 处理单条查询结果的消费函数,不能为空
+ * @throws NullPointerException 如果 consumer 为 null 抛出
+ */
+ public SimpleResultHandler(Consumer consumer) {
+ if (consumer == null) {
+ throw new NullPointerException("consumer不能为null");
+ }
+ this.consumer = consumer;
+ }
+
+ /**
+ * MyBatis 查询结果回调方法,
+ * 每接收到一条查询结果即调用消费函数进行处理
+ *
+ * @param context 查询结果上下文,包含当前单条结果对象
+ */
+ @Override
+ public void handleResult(ResultContext extends T> context) {
+ T result = context.getResultObject();
+ consumer.accept(result);
+ }
+}
--
Gitee
From 3db7f8ce74ebb8dd447388628ef1c59be340785c Mon Sep 17 00:00:00 2001
From: AprilWind <2100166581@qq.com>
Date: Sun, 3 Aug 2025 16:22:53 +0800
Subject: [PATCH 2/2] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=E8=BF=87=E6=BB=A4?=
=?UTF-8?q?=E5=A4=84=E7=90=86=E5=99=A8=E4=BD=BF=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../mybatis/core/mapper/BaseMapperPlus.java | 33 +++++++++++++++++++
.../mybatis/handler/BatchResultHandler.java | 8 -----
.../mybatis/handler/FilterResultHandler.java | 7 ----
.../mybatis/handler/SimpleResultHandler.java | 4 ---
4 files changed, 33 insertions(+), 19 deletions(-)
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
index 0d777f4e0..9785b0832 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java
@@ -15,12 +15,15 @@ import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StreamUtils;
+import org.dromara.common.mybatis.handler.FilterResultHandler;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
+import java.util.function.Predicate;
/**
* 自定义 Mapper 接口, 实现 自定义扩展
@@ -355,4 +358,34 @@ public interface BaseMapperPlus extends BaseMapper {
return StreamUtils.toList(this.selectObjs(wrapper), mapper);
}
+ /**
+ * 根据查询条件和过滤条件查询数据,并将结果转换为指定的VO类型列表
+ *
+ * @param wrapper 查询条件Wrapper
+ * @param filter 结果过滤条件
+ * @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回
+ */
+ default List selectFilterVoList(Wrapper wrapper, Predicate filter) {
+ return this.selectFilterVoList(wrapper, filter, this.currentVoClass());
+ }
+
+ /**
+ * 根据查询条件和过滤条件查询数据,并将结果转换为指定的VO类型列表
+ *
+ * @param wrapper 查询条件Wrapper
+ * @param filter 结果过滤条件
+ * @param voClass 要转换的VO类的Class对象
+ * @param VO类的类型
+ * @return 查询到的符合条件的对象列表,经过转换为指定类型的对象后返回
+ */
+ default List selectFilterVoList(Wrapper wrapper, Predicate filter, Class voClass) {
+ List list = new ArrayList<>();
+ // 使用过滤处理器过滤数据,并将符合条件的结果加入 list
+ this.selectList(wrapper, new FilterResultHandler<>(filter, list::add));
+
+ if (CollUtil.isEmpty(list)) {
+ return CollUtil.newArrayList();
+ }
+ return MapstructUtils.convert(list, voClass);
+ }
}
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java
index bd505a038..cfa7a6233 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/BatchResultHandler.java
@@ -59,16 +59,8 @@ public class BatchResultHandler implements ResultHandler {
*
* @param batchConsumer 批量消费函数,不能为null
* @param batchSize 批量大小,必须大于0
- * @throws NullPointerException batchConsumer为null时抛出
- * @throws IllegalArgumentException batchSize小于等于0时抛出
*/
public BatchResultHandler(Consumer> batchConsumer, int batchSize) {
- if (batchConsumer == null) {
- throw new NullPointerException("batchConsumer不能为null");
- }
- if (batchSize <= 0) {
- throw new IllegalArgumentException("batchSize必须大于0");
- }
this.batchConsumer = batchConsumer;
this.batchSize = batchSize;
this.buffer = new ArrayList<>(batchSize);
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java
index 6496458d6..0c9096700 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/FilterResultHandler.java
@@ -35,15 +35,8 @@ public class FilterResultHandler implements ResultHandler {
*
* @param filter 过滤条件
* @param consumer 满足过滤条件时的处理函数
- * @throws NullPointerException 如果 filter 或 consumer 为 null
*/
public FilterResultHandler(Predicate filter, Consumer consumer) {
- if (filter == null) {
- throw new NullPointerException("过滤条件,不能为空");
- }
- if (consumer == null) {
- throw new NullPointerException("处理函数,不能为空");
- }
this.filter = filter;
this.consumer = consumer;
}
diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java
index c5950da1a..13dc06010 100644
--- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java
+++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/handler/SimpleResultHandler.java
@@ -25,12 +25,8 @@ public class SimpleResultHandler implements ResultHandler {
* 构造 SimpleResultHandler 实例
*
* @param consumer 处理单条查询结果的消费函数,不能为空
- * @throws NullPointerException 如果 consumer 为 null 抛出
*/
public SimpleResultHandler(Consumer consumer) {
- if (consumer == null) {
- throw new NullPointerException("consumer不能为null");
- }
this.consumer = consumer;
}
--
Gitee