概述
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());
}
}
测试结果示例
