MyBatis 的 ResultHandler 是流式回调机制,用于大数据量、低延迟等场景,需满足返回 void、显式传入 ResultHandler 实例、SQL 中指定 resultHandlerType 三个前提。

MyBatis 的 ResultHandler 不是用来替代 resultType 或 resultMap 的,而是为“不想要全部结果进内存”或“需要边查边处理”的场景服务的。它本质是流式回调机制——每读取一行就触发一次 handleResult,适合大数据量、低延迟响应、分批落库、实时转换等复杂需求。
必须满足的三个硬性前提
很多问题出在配置没对,导致 ResultHandler 根本不执行:
- Mapper 接口方法返回值必须是 void(不是 List、Map 或其他类型)
- 方法参数列表中必须显式传入 ResultHandler<T> 实例(不能靠 Spring 自动注入或全局配置)
- SQL 映射语句中要指定 resultHandlerType(XML 中)或用 @Options(resultHandlerType = ...) 注解(注解方式)
处理多级嵌套与动态字段的技巧
当查询返回 Map 或 JSON 字段,且结构不固定时,ResultHandler 比 resultType 更灵活:
- 在 handleResult 中直接拿到
context.getResultObject(),它可能是LinkedHashMap<String, Object>,可按需提取 key、做类型判断、跳过空字段 - 配合 Jackson 或 Fastjson,把 Map 转成泛型对象或树形结构(如 JsonNode),再做字段校验、脱敏、路由分发
- 若字段名含下划线需转驼峰,可在 handler 内部统一做 key 映射,无需依赖 global-config 的 mapUnderscoreToCamelCase
结合业务逻辑做轻量聚合与中断控制
ResultHandler 支持边处理边决策,比如查用户列表时只取前 1000 条有效数据并提前终止:
- 用
context.getResultCount()统计已处理行数,达到阈值后调用context.stop() - 在 handleResult 中做业务过滤(如 status=1、create_time > 7天),只 add 到本地 list 或写入 Kafka,不缓存无效数据
- 异常不抛出,而是记录日志 + 计数器,避免单条脏数据导致整个查询失败
与 Spring 环境协同的关键点
在 Spring Boot 项目里,ResultHandler 常被误当成普通 Bean 注入使用,实际应按需构造:
- 不要把自定义 ResultHandler 声明为 @Component,它生命周期短、状态私有,每次查询都应 new 一个新实例
- 若需访问 Service 或 Mapper,可通过构造函数传入,但注意避免循环依赖;更推荐把业务逻辑封装成工具类,在 handleResult 中调用
- 事务边界仍由外层 @Transactional 控制,ResultHandler 内部的 DB 写入操作需单独开事务(如用 TransactionTemplate)


















