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工具上传网页更新内容【可选】 四、实验总结 …...
前后端登录的密码加密和解密
在一个典型的前后端应用中,前端对密码进行加密后传给后端,后端再进行解密或验证。这通常涉及前端加密、后端解密或验证的相互配合。下面是一个基本的流程: 前端加密: 前端可以使用各种加密库或算法对密码进行加密。常见的是使用哈…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
MySQL中【正则表达式】用法
MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现(两者等价),用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例: 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
