mybatis 的 resulthandler 是处理超大数据集流式查询的核心机制,通过逐行回调避免全量加载、降低 oom 风险;需满足驱动支持、fetchsize 设为 integer.min_value、url 启用流式参数、mapper 返回 void 并接收 resulthandler 参数、resulttype 明确且不用 resultmap。

MyBatis 的 ResultHandler 是处理超大数据集流式查询的核心机制,它避免将全部结果一次性加载到内存,而是逐行回调处理,显著降低 OOM 风险。
启用流式查询的前提条件
流式查询依赖 JDBC 驱动的底层支持,必须满足以下几点:
- 使用支持流式游标的数据库驱动(如 MySQL 的
mysql-connector-java 8.0+,PostgreSQL 的pgjdbc) - SQL 执行需开启
fetchSize = Integer.MIN_VALUE(即流式模式),MyBatis 会自动设置该值当使用ResultHandler时 - 数据库连接 URL 中需显式启用流式参数(例如 MySQL:添加
&useCursorFetch=true&defaultFetchSize=-2147483648) - Mapper 方法返回类型必须为
void,且第二个参数为ResultHandler<t></t>
定义 Mapper 接口与 SQL
Mapper 接口方法签名要严格匹配流式要求:
Java 接口:
void selectLargeData(@Param("condition") String condition, ResultHandler<user> handler);</user>
XML 映射:
<select id="selectLargeData" resulttype="User">
SELECT id, name, email FROM user WHERE status = #{condition}
</select>
注意:resultType 必须明确(不能用 resultMap 的嵌套复杂映射,否则可能触发缓存或批量解析,破坏流式语义)
实现 ResultHandler 进行逐行处理
ResultHandler 是函数式接口,只需实现 handleResult 方法,在每次获取一行数据时被调用:
sqlSession.select("mapper.selectLargeData", "active", new ResultHandler<user>() {
@Override
public void handleResult(ResultContext<user> context) {
User user = context.getResultObject();
// ✅ 安全操作:写入文件、发消息、批量入库(控制批次大小)
processUser(user);
// ⚠️ 注意:不要在 handler 中调用 context.next() 或修改 context
// 不要在此处调用 sqlSession.commit() 等会干扰流式游标的操作
}
});</user></user>
常见安全实践:
- 每处理 N 条后做一次批提交(如写入 Kafka 或插入另一张表),避免堆积
- 使用线程安全的收集器(如
ConcurrentLinkedQueue)暂存中间结果 - 若需统计总数,用
context.getResultSet().getRow()不可靠,应单独查 count 或在 handler 中累加计数器
注意事项与避坑点
流式查询看似简单,但容易因配置或用法不当退化为全量加载:
- Spring 管理的
SqlSession默认是ExecutorType.SIMPLE,不支持流式;需确保使用ExecutorType.REUSE或BATCH(但实际流式只与SIMPLE兼容,MyBatis 内部会强制适配) - 不要在
ResultHandler中抛出未捕获异常,会导致游标提前关闭、部分数据丢失 - Oracle 需额外设置
resultSetType=TYPE_FORWARD_ONLY和resultSetConcurrency=CONCUR_READ_ONLY - MyBatis-Plus 的
lambdaQuery().execute(...)封装不支持原生流式,需回退到原生SqlSession调用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











