myabtis流式查询
1、流式查询简介
流式处理在大数据方面应用比较广泛。随着数据的爆发式增长,流式处理的方式也被应用到日常的工具中,如JDK的对于集合处理的Stream流、Redis5.0新增的数据结构Stream专门来处理消息等。
流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。
今天要分享的是Mybaits的流式查询。Mybatis流式查询通过使用游标一次处理一条数据的方式,减少了内存的占用,从而提高了大数据量查询的效率。
2、Mybaits流式查询
Mybaits
在3.4.0
版本增加了流式查询也就是游标Cursor
。更新日志传动门,Issues · mybatis/mybatis-3 · GitHub
2.1 游标简介
游标的使用和List一样,只不过返回的结果不同。
游标继承
Closeable
,Iterable
接口,所以游标是可关闭、可遍历的。包含三个方法:
isOpen
游标的开关是否打开
isConsumed
通过游标是否将拉取的数据消费完,也就是是否将数据全部取完。
getCurrentIndex
获取当前数据的位置,位置是从0开始的。
2.2 游标的使用
定义简单的查询方法,这里使用注解
@Select
的方式代替Xml
,方便测试。
@Select("select * from logan_log.logan_login")
Cursor<LoganLogin> testCursorQuery();
编写测试类。因为游标是可关闭的,所以使用完之后要想流一样关闭掉。这里采用
try-resource
的方式简化流的关闭。
@Test
void testCursorSql() {try (Cursor<LoganLogin> cursor = loganLoginMapper.testCursorQuery()) {cursor.forEach((ll) -> {System.out.println("ll:" + JSON.toJSONString(ll));System.out.println("ll cursor:" + cursor.getCurrentIndex());});} catch (IOException e) {throw new RuntimeException(e);}
}
处理结果,报错了。。。
原因分析:
-
A Cursor is already closed.
还没有处理数据,查询完数据之后游标被关闭了。 -
由于Mybatis帮我们做了自动事务的提交。查询完毕之后就会提交事务关闭连接,游标自然也被关闭了,就会出现这样的问题。游标需要占用数据库的连接,然后逐条的将数据返回的。
解决方案:
-
将整个方法放在一个事务里面,处理完数据之后在提交事务。比较暴力的解决方案就是使用声明式事务
@Transactional
。大事务的处理可能会影响业务的并发。 -
采用小颗粒度的编程式事务
TransactionTemplate
,只针对流式查询的结果包装到一个事务里面即可。 -
也可以采用手动提交事务,使用
SqlSessionFactory
手动建立连接和关闭。
编程式事务解决问题可能更方便一些,所以采用编程式事务的方法解决。
@Test
void testCursorSql2() {transactionTemplate.execute(new TransactionCallbackWithoutResult() {@Overrideprotected void doInTransactionWithoutResult(TransactionStatus status) {System.out.println("TransactionStatus1:" + status.hasSavepoint());try (Cursor<LoganLogin> cursor = loganLoginMapper.testCursorQuery()) {cursor.forEach((ll) -> {System.out.println("ll:" + JSON.toJSONString(ll));System.out.println("ll cursor:" + cursor.getCurrentIndex());});System.out.println("TransactionStatus2:" + status.hasSavepoint());} catch (IOException e) {throw new RuntimeException(e);}}});
}
结果正常打印
2.3 游标查询的升级
游标的使用总会便随着事务,对于开发者来说成了黄金搭档。能不能将其封装成一个方法来方便开发者调用呢,还真有。就是使用
ResultHandler
参数。有人就提出了对应的issues,同时也在
mybatis-3.4.6
得到了支持。
2.3.1 ResultHandler参数说明
ResultHandler
作为参数参数传递,返回值为Void。xml的配置需要指定ResultType
。
查询到的结果,也是封装在
ResultHandler
里面的ResultContext
中。
T getResultObject()
返回的数据结果
int getResultCount()
返回当前结果的记录数据。
boolean isStopped()
是否停止拉取数据
void stop()
中断数据拉取
2.3.2 ResultHandler的使用
ResultHandler
的使用,官方文档中已经给出使用案例。我们来测试一下。先定义一下方法。同样采用注解的方式替代
xml
。
@ResultType(LoganLogin.class)
@Select("select * from logan_log.logan_login")
void testStreamQuery(ResultHandler<LoganLogin> handler);
其中
ResultType
必须要指定,否则就会报错。
测试案例
@Test
void testStreamSql() {loganLoginMapper.testStreamQuery(resultContext -> {System.out.println("handleResult:" + JSON.toJSONString(resultContext.getResultObject()));System.out.println("ResultCount:" + resultContext.getResultCount());System.out.println("isStop:" + resultContext.isStopped());System.out.println("---------------------------------------");});
}
测试结果
2.4 fetchSize
的讨论
很多技术博客中都强调,fetchSize
可以控制每次拉取的数量。
但是笔者配置,测试之后发现并没有什么效果。在使用流式查询时,官方文档中也没有说明必须要配置。
元芳,你怎么看???
3、流式查询在Mybatis-Plus中的使用
mybatis中已经支持了流式查询,那么好用的MP怎么会不支持。MP在
3.5.4
中也得到了支持。
具体的方法如下:
/*** 根据 entity 条件,查询全部记录** @param queryWrapper 实体对象封装操作类(可以为 null)* @param resultHandler 结果处理器 {@link ResultHandler}* @since 3.5.4*/
void selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<T> resultHandler);/*** 根据 entity 条件,查询全部记录(并翻页)* @param page 分页查询条件* @param queryWrapper 实体对象封装操作类(可以为 null)* @param resultHandler 结果处理器 {@link ResultHandler}* @since 3.5.4*/
void selectList(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<T> resultHandler);
/*** 根据 Wrapper 条件,查询全部记录** @param queryWrapper 实体对象封装操作类* @param resultHandler 结果处理器 {@link ResultHandler}* @since 3.5.4*/
void selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<Map<String, Object>> resultHandler);
/*** 根据 Wrapper 条件,查询全部记录(并翻页)** @param page 分页查询条件* @param queryWrapper 实体对象封装操作类* @param resultHandler 结果处理器 {@link ResultHandler}* @since 3.5.4*/
void selectMaps(IPage<? extends Map<String, Object>> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<Map<String, Object>> resultHandler);
/*** 根据 Wrapper 条件,查询全部记录* <p>注意: 只返回第一个字段的值</p>** @param queryWrapper 实体对象封装操作类(可以为 null)* @param resultHandler 结果处理器 {@link ResultHandler}* @since 3.5.4*/
<E> void selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper, ResultHandler<E> resultHandler);
就拿最典型的
selectList
来测试。没有分页的查询。其中Wrapper主要是查询的
Sql
的条件拼接,也可以是空。ResultHandler
就是我们的流式返回了。
@Test
void testStreamPlus() {loganLoginMapper.selectList(Wrappers.emptyWrapper(), resultContext->{System.out.println("handleResult:" + JSON.toJSONString(resultContext.getResultObject()));System.out.println("ResultCount:" + resultContext.getResultCount());System.out.println("isStop:" + resultContext.isStopped());System.out.println("---------------------------------------");});
}
测试结果
有分页的查询和无分页方法是重载关系,增加了分页参数
Page
。其中Page
只是限定了拉取数据的总条数,但是并不会自动封装到Page
对象中。主要使用了,resultContext
的stop
的方法。
@Test
void testStreamPage() {Page<LoganLogin> page = new Page<>(1, 2);loganLoginMapper.selectList(page, Wrappers.emptyWrapper(), resultContext->{System.out.println("当前处理第" + resultContext.getResultCount() + "条记录.");System.out.println("handleResult:" + JSON.toJSONString(resultContext.getResultObject()));System.out.println("isStop:" + resultContext.isStopped());System.out.println("---------------------------------------");});System.out.println(JSON.toJSONString(page.getRecords()));
}
测试结果
相关文章:

myabtis流式查询
1、流式查询简介 流式处理在大数据方面应用比较广泛。随着数据的爆发式增长,流式处理的方式也被应用到日常的工具中,如JDK的对于集合处理的Stream流、Redis5.0新增的数据结构Stream专门来处理消息等。 流式查询指的是查询成功后不是返回一个集合而是返回…...

K8S的pod创建过程
创建流程图 用户发起请求创建deployment;apiserver收到创建资源的请求,apiserver对客户端操作进行身份认证,认证完成后接受该请求,并把相关的信息保存到etcd中,然后返回确认信息给客户端;apiserver向etcd…...

java修仙传之海岛奇遇
昨日开会, 商量了一下接口返回数据, 要求统一, 之前也同意,直接抛异常, 现在觉得之前那个异常不好, 看着不美观,对客户不友好 要求重新做。 大概要求如下: 要求1:范…...

电子商务平台对接电商供应链,不得不说的开放平台电商API接口
B2B电商开放平台的设计需要从以下几面去思考: 开放平台API接口的设计,主要是从功能需求的角度,设计满足业务需求的接口及对应的字段; 平台与商家之间信息的对接,对接的方法有哪些?对接过程中需要可能会遇到…...

【JAVA学习笔记】 57 - 本章作业
项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter14/src/com/yinhai/homework 1. (1)封装个新闻类,包含标题和内容属性,提供get, set方法, 重写toString方法,打印对象时只打印标题; (2)只提供…...
【题解】[GenshinOI Round 3] P9816 少项式复合幂
题目链接 分析 首先这题给了很大的提示信息 注意 m 和 p 的范围 , 很自然的想到可以先把所有可能的 f ( x ) f(x) f(x) 算出来. 思维误区 有些人在算完 f ( x ) f(x) f(x) 之后可能就会去思考找环的问题,然后一些码力弱的大佬就会祭掉. 在经过仔细的观察之后…...

手写数字识别--神经网络实验
实验源码自取: 神经网络实验报告源码.zip - 蓝奏云 上深度学习的课程,老师布置了一个经典的实验报告,我做了好久才搞懂,所以把实验报告放到CSDN保存,自己忘了方便查阅,也为其他人提供借鉴 由于本人是小白…...

双11消费遇冷?如何让消费回归心智原点
近一年来,小红书话题「重新养育自己」引热议。直面成长缺憾,不少人探寻解决方案,即像对待新生命般,不论是衣食住行还是心灵,重新关照自己。 借此,本期千瓜将锁定小红书热门话题背后的消费观转变࿰…...

一分钟了解:什么是Image Matting?
1. 基本概念 Image Matting是图像处理领域的一个基本任务,意为“图像背景抠出”或者“抠图”。这项任务在图像处理、影视制作领域广泛应用。比如,拍电影时常用的扣绿,就是演员在绿幕前面表演,后期再把人物抠出来放到一个新的背景…...

微信小程序 跳转客服页面
前言 小程序 用户反馈 没有页面设计 可以直接跳转小程序指定客服页面 <button class"contactBtn"open-type"contact" contact"handleContact" session-from"sessionFrom">...
10个简单增删改查的免费Spring Boot源代码项目
此页面包含用于学习目的的免费 Spring boot 项目列表。每个 Spring boot 项目的源代码都托管在 GitHub 存储库上,因此您可以免费下载或克隆源代码并亲身体验 Spring boot 框架。 1.员工管理应用程序(ReactJS Spring Boot CRUD全栈应用程序) …...
mysql数据表设计
命名 mysql表名的命名规范为表名可以用 t_ 、tb_的前缀,或者是业务模块前缀。比如t_order。 有些项目也会使用 tt_、tm_、 ts_ 等前缀,根据项目的习惯命名就好了。 主键: AUTO_INCREMENT 表示自增,UNSIGNED 表示无符号…...

pytorch复现4_Resnet
ResNet在《Deep Residual Learning for Image Recognition》论文中提出,是在CVPR 2016发表的一种影响深远的网络模型,由何凯明大神团队提出来,在ImageNet的分类比赛上将网络深度直接提高到了152层,前一年夺冠的VGG只有19层。Image…...

【数据库】形式化关系查询语言(一):关系代数Relational Algebra:基本运算、附加关系代数、扩展的关系代数
目录 一、关系代数Relational Algebra 1. 基本运算 a. 选择运算(Select Operation) b. 投影运算(Project Operation) 组合 c. 并运算(Union Operation) d. 集合差运算(Set Difference Op…...
【计算机网络】计算机网络和因特网
一.基本术语介绍 端系统通过通信链路(communication link)和分组交换机(packet switch)连接到一起,连接这些端系统和分组交换机的物理媒体包括:同轴电缆,铜线,光纤和无线电频谱。而…...

JAVA面经整理(9)
一)什么是Spring?它有什么优点? spring是一款顶级的开源框架,他是包含了众多工具方法的IOC容器,Spring中包含了很多模块,比如说Spring-core,Spring-context,Spring-aop,Spring-web,…...
IPD(集成产品开发)模式下的产品研发流程
IPD(集成产品开发)涵盖了产品从创意提出到研发、生产、运营等,包含了产品开发到营销运营的整个过程。围绕产品(或项目)生命周期的过程的管理模式,是一套生产流程,更是时下国际先进的管理体系。I…...

Flutter GetX的使用
比较强大的状态管理框架 引入库: dependencies:get: ^4.6.6一.实现一个简单的demo 实现一个计数器功能 代码如下: import package:flutter/material.dart; import package:get/get.dart;void main() > runApp(const GetMaterialApp(home: Home()…...

【Amazon】AWS实战 | 快速发布安全传输的静态页面
文章目录 一、实验架构图二、实验涉及的AWS服务三、实验操作步骤1. 创建S3存储桶,存放网站网页2. 使用ACM建立域名证书3. 设置Cloudfront,连接S3存储桶✴️4. 设置Route53,解析域名服务5. 通过CLI工具上传网页更新内容【可选】 四、实验总结 …...
前后端登录的密码加密和解密
在一个典型的前后端应用中,前端对密码进行加密后传给后端,后端再进行解密或验证。这通常涉及前端加密、后端解密或验证的相互配合。下面是一个基本的流程: 前端加密: 前端可以使用各种加密库或算法对密码进行加密。常见的是使用哈…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

QT: `long long` 类型转换为 `QString` 2025.6.5
在 Qt 中,将 long long 类型转换为 QString 可以通过以下两种常用方法实现: 方法 1:使用 QString::number() 直接调用 QString 的静态方法 number(),将数值转换为字符串: long long value 1234567890123456789LL; …...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

Linux 中如何提取压缩文件 ?
Linux 是一种流行的开源操作系统,它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间,使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的,要在 …...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...