MyBatis 流式查询实战:ResultHandler 处理海量数据

概述

Mybatis 的默认查询行为是"全量加载":JDBC 驱动把整个 ResultSet 缓存到客户端内存,Mybatis 再逐行映射成 Java 对象塞进 List。数据量少的时候没有问题,但是数据量一旦多起来,这就是一颗内存炸弹

其中一个解决办法就是 Mybatis 的 ResultHandler 流式查询。它让结果集不再一次性加载,而是每读取一条数据就回调一次 handleResult 方法,处理完立即释放

本文包含完整的测试代码:XML 配置、Mapper 接口定义、Service 回调实现、测试类调用。拿来就能跑,跑通就能用。

示例

SQL XML 配置文件

xml 复制代码
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="wxw.mengyuan.dsf.entity.operations.systemmonitor.thread.mapper.ThreadMapper">
  <!-- 流式查询 -->  <!-- fetchSize:设置为 Integer 的最小值,代表开启流式查询 -->
  <select id="streamQuery" resultType="wxw.mengyuan.dsf.entity.operations.systemmonitor.thread.entity.ThreadEntity" fetchSize="-2147483648">
    SELECT * FROM dsf_thread WHERE COLLECT_TIME BETWEEN #{start} AND #{end} ORDER BY  COLLECT_TIME DESC, ID DESC
  </select>
</mapper>

Mapper 数据类

java 复制代码
/** * 线程信息数据类 */
@Mapper
@Repository
public interface ThreadMapper extends BaseMapper<ThreadEntity> {
  /**
   * 流式读取
   * @param start 开始时间
   * @param end 结束时间
   * @param handler 结果集处理器
   */
  // 注意事项
  // (1) 返回结果必须为 void
  // (2) 在传入参数之后,必须使用 ResultHandler 对象接收返回结果
  void streamQuery(@NonNull @Param("start") LocalDateTime start, @NonNull @Param("end") LocalDateTime end, ResultHandler<SqlLogEntity> handler);
}

Service 业务类

java 复制代码
/**
 * 线程信息业务类
 */
@Slf4j
@Service
public class ThreadService extends ServiceImpl<ThreadMapper, ThreadEntity> {

  /**
   * 流式读取
   * @param start 开始时间
   * @param end 结束时间
   */
  public void streamQuery(@NonNull LocalDateTime start, @NonNull LocalDateTime end) {
    long startTime = System.currentTimeMillis();
    AtomicInteger count = new AtomicInteger(0);
    this.baseMapper.streamQuery(start, end, new ResultHandler<SqlLogEntity>() {
      @Override
      public void handleResult(ResultContext<? extends SqlLogEntity> resultContext) {
        // 打印日志        log.warn("当前已经遍历了 {} 条数据", count.incrementAndGet());
        // 获取一条数据:包装好的实体类对象
        // System.out.println(resultContext.getResultObject());
        // 满足某种条件时,停止数据获取(关闭ResultSet,后续不会再进入当前回调函数)
        if (count.get() >= 1000000) {
          resultContext.stop();
        }
      }
    });
    log.warn("耗时 {} 毫秒", System.currentTimeMillis() - startTime);
  }
}

测试类

java 复制代码
/**
 * Mybatis案例
 */
@SpringBootTest(classes = OperationsApplication.class)
public class MybatisDemo {
  /** 线程信息业务类 */
  @Autowired
  private ThreadService threadService;
  @Test
  public void mybatisTest() {
    threadService.streamQuery(LocalDateTime.now().minusYears(1), LocalDateTime.now());
  }
}

测试结果示例

相关推荐
刚入门的大一新生9 分钟前
Linux-线程互斥
java·开发语言
赵小哥要冲啦41 分钟前
社区邻里互助小程序从 0 到 1 复盘:需求拆解与信任机制设计
java·小程序·需求分析
xiaogai_gai1 小时前
采购业务退款单同步实战:从钉钉审批流到金蝶云星空付款单
java·数据库·钉钉
二哈赛车手1 小时前
新人笔记---hermes定时功能设计,具体讲解以及可优化点
java·人工智能·笔记·spring·ai·typescript
ZWZhangYu1 小时前
从 Dify 实践理解大模型上下文缓存
java·后端·spring
小羊没烦恼!2 小时前
关于分布式锁原理的一些学习与思考-redis分布式锁,zookeeper分布式锁
java·服务器·网络·学习·负载均衡
Fuyo_11193 小时前
C++ AVL树保姆级详解:从平衡因子到四大旋转机制
java·c++·算法
Sarvartha3 小时前
Date、LocalDate、LocalTime、LocalDateTime、DateTimeFormatter类
java
阿俊-全栈开发3 小时前
LikeShop 多商户 Java 版商城微服务模块拆分与部署架构演进实践
java·微服务·架构·likeshop开源商城·多商户源码
Wang's Blog4 小时前
Java 项目实战:交友项目 - 联系人列表
java·开发语言·交友