前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MyBatis 如何实现流式查询?

MyBatis 如何实现流式查询?

作者头像
码农架构
发布于 2022-04-13 08:55:30
发布于 2022-04-13 08:55:30
1.2K00
代码可运行
举报
文章被收录于专栏:码农架构码农架构
运行总次数:0
代码可运行

导读:本文主要围绕MyBatis流式查询方式进行总结,通过里流式查降低内存使用。总结本篇文章希望对从事相关工作的同学能够有所帮助或者启发 。

流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。

流式查询的好处是能够降低内存使用。如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询。

而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询。因此流式查询是一个数据库访问框架必须具备的功能。

流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭。

一、MyBatis 流式查询接口


MyBatis 提供了一个叫 org.apache.ibatis.cursor.Cursor 的接口类用于流式查询,这个接口继承了 java.io.Closeable 和 java.lang.Iterable 接口。

由此可知:

  • Cursor 是可关闭的。实际上当关闭 Cursor 时,也一并将数据库连接关闭了。
  • Cursor 是可遍历的。

除此之外,Cursor 还提供了三个方法:

  • isOpen():用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据。
  • isConsumed():用于判断查询结果是否全部取完。
  • getCurrentIndex():返回已经获取了多少条数据。

因为 Cursor 实现了迭代器接口,因此在实际使用当中,从 Cursor 取数据非常简单:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try(Cursor cursor = mapper.querySomeData()) {
    cursor.forEach(rowObject -> {
        // ...
    });
}

使用 try-resource 方式可以令 Cursor 自动关闭。但构建 Cursor 的过程不简单。

二、如何构建 Cursor


我们举个实际例子。下面是一个 Mapper 类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Mapper
public interface FooMapper {
    @Select("select * from foo limit #{limit}")
    Cursor<Foo> scan(@Param("limit") int limit);
}

方法 scan() 是一个非常简单的查询。我们在定义这个方时,指定返回值为 Cursor 类型,MyBatis 就明白这个查询方法是一个流式查询。

然后我们再写一个 SpringMVC Controller 方法来调用 Mapper(无关的代码已经省略):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GetMapping("foo/scan/0/{limit}")
public void scanFoo0(@PathVariable("limit") int limit) throws Exception {
    try (Cursor<Foo> cursor = fooMapper.scan(limit)) {  // 1
        cursor.forEach(foo -> {});                      // 2
    }
}

假设 fooMapper 是 @Autowired 进来的。注释 1 处是获取 Cursor 对象并保证它能最后关闭;2 处则是从 cursor 中取数据。

上面的代码看上去没什么问题,但是执行 scanFoo0(int) 时会报错:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
java.lang.IllegalStateException: A Cursor is already closed.

这是因为我们前面说了在取数据的过程中需要保持数据库连接,而 Mapper 方法通常在执行完后连接就关闭了,因此 Cusor 也一并关闭了。

所以,解决这个问题的思路不复杂,保持数据库连接打开即可。我们至少有三种方案可选。

| 方案一:SqlSessionFactory

我们可以用 SqlSessionFactory 来手工打开数据库连接,将 Controller 方法修改如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GetMapping("foo/scan/1/{limit}")
public void scanFoo1(@PathVariable("limit") int limit) throws Exception {
    try (
        SqlSession sqlSession = sqlSessionFactory.openSession();  // 1
        Cursor<Foo> cursor = 
              sqlSession.getMapper(FooMapper.class).scan(limit)   // 2
    ) {
        cursor.forEach(foo -> { });
    }
}

上面的代码中,1 处我们开启了一个 SqlSession (实际上也代表了一个数据库连接),并保证它最后能关闭;2 处我们使用 SqlSession 来获得 Mapper 对象。这样才能保证得到的 Cursor 对象是打开状态的。

| 方案二:TransactionTemplate

在 Spring 中,我们可以用 TransactionTemplate 来执行一个数据库事务,这个过程中数据库连接同样是打开的。

代码如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GetMapping("foo/scan/2/{limit}")
public void scanFoo2(@PathVariable("limit") int limit) throws Exception {
    TransactionTemplate transactionTemplate = 
            new TransactionTemplate(transactionManager);  // 1

    transactionTemplate.execute(status -> {               // 2
        try (Cursor<Foo> cursor = fooMapper.scan(limit)) {
            cursor.forEach(foo -> { });
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    });
}

上面的代码中,1 处我们创建了一个 TransactionTemplate 对象(此处 transactionManager 是怎么来的不用多解释,本文假设读者对 Spring 数据库事务的使用比较熟悉了),2 处执行数据库事务,而数据库事务的内容则是调用 Mapper 对象的流式查询。

注意:这里的 Mapper 对象无需通过 SqlSession 创建。

| 方案三:@Transactional 注解

这个本质上和方案二一样,代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@GetMapping("foo/scan/3/{limit}")
@Transactional
public void scanFoo3(@PathVariable("limit") int limit) throws Exception {
    try (Cursor<Foo> cursor = fooMapper.scan(limit)) {
        cursor.forEach(foo -> { });
    }
}

它仅仅是在原来方法上面加了个 @Transactional 注解。这个方案看上去最简洁,但请注意 Spring 框架当中注解使用的坑:只在外部调用时生效。在当前类中调用这个方法,依旧会报错。

以上是三种实现 MyBatis 流式查询的方法。

- END -

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-03-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
还在用分页?太Low !试试 MyBatis 流式查询,真心强大!
流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。
路人甲Java
2021/08/20
4.4K0
新技能 MyBatis 千万数据表,快速分页!
流式查询 指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。
肉眼品世界
2020/11/17
6020
新技能 MyBatis 千万数据表,快速分页!
新技能 MyBatis 千万数据表,快速分页!
流式查询 指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。
java进阶架构师
2020/12/03
3710
新技能 MyBatis 千万数据表,快速分页!
新技能 MyBatis 千万数据表,快速分页!
流式查询 指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。
程序员白楠楠
2020/11/28
7050
mybatis流式查询
https://gitee.com/VampireAchao/simple-mybatis.git
阿超
2022/08/21
8100
mybatis流式查询
MyBatis Plus 解决大数据量查询慢问题
在实际工作中当指定查询数据过大时,我们一般使用分页查询的方式一页一页的将数据放到内存处理。但有些情况不需要分页的方式查询数据或分很大一页查询数据时,如果一下子将数据全部加载出来到内存中,很可能会发生OOM(内存溢出);而且查询会很慢,因为框架耗费大量的时间和内存去把数据库查询的结果封装成我们想要的对象(实体类)。
捡田螺的小男孩
2023/02/22
1.8K0
MyBatis Plus 解决大数据量查询慢问题
【面试专题】Mybatis高频面试题
1.系统启动的时候会加载解析全局配置文件和对应映射文件。加载解析的相关信息存储在 Configuration 对象
程序员波特
2024/04/02
1270
【面试专题】Mybatis高频面试题
使用MyBatis缓存的简单案例
MyBatis 是一种流行的持久层框架,支持 SQL 的自定义执行、映射以及复杂查询。MyBatis 提供了两级缓存机制:一级缓存和二级缓存。一级缓存是基于 SqlSession 的缓存,而二级缓存则是跨 SqlSession 的缓存。使用缓存可以大大提高查询的性能,因为重复的查询不会每次都访问数据库,而是从缓存中获取结果。
闻说社
2024/10/24
2010
使用MyBatis缓存的简单案例
Spring的基本配置和Spring与Mybatis的整合
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IOC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架
全栈程序员站长
2022/08/11
3240
Spring的基本配置和Spring与Mybatis的整合
什么是MyBatis?
MyBatis 是一款 开源的持久层框架,用于简化 Java 应用与数据库之间的交互。它最初名为 iBatis,2010 年由 Apache 迁移至 GitHub 并更名为 MyBatis。
用户11634972
2025/04/29
2880
MyBatis 源码学习笔记(一)- MyBatis概述
上述代码中使用Statement对象执行的查询,如果SQL语句中有参数占位符建议使用PreparedStatement对象来执行查询,PreparedStatement对象可以对SQL语句进行预编译,可以放置SQL注入;而且性能相对Statement会更好些。Jdbc针对数据库写操作需要对事务进行提交。
RiemannHypothesis
2022/08/19
2400
MyBatis 源码学习笔记(一)- MyBatis概述
MyBatis 从入门到放弃 ( MyBatis基础总结 )
MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架包括SQL Maps和Data Access Objects(DAO)。
愷龍
2022/11/12
9960
MyBatis 从入门到放弃 ( MyBatis基础总结 )
SSM学习笔记之MyBatis
ORM(Object Relation Mapping)对象关系映射,将Java中的一个对象与数据表中的一行记录一一对应。
Jetpropelledsnake21
2022/05/11
5430
SSM学习笔记之MyBatis
MyBatis笔记
**API接口:**提供给外部使用的API接口,开发人员使用本地API接口操作数据库。接口层一收到调用请求,就会调用数据处理层进行具体的数据处理。
腿子代码了
2023/12/24
3320
MyBatis笔记
MyBatis详解
在mybatis-config.xml文件中,可以通过以下配置进行MyBatis事务管理:<transactionManager type="JDBC/MANAGED" />
CODER-V
2023/03/04
2K0
MyBatis详解
Mybatis基础
这里的 <log4j:configuration xmlns:log4j=”http://jakarta.apache.org/log4j/"> 在 Idea 中会报错
OY
2022/03/17
1.1K0
Mybatis基础
Spring Boot集成MyBatis的2种方式
最近总是有同事和技术群的朋友提问在Spring Boot中使用MyBatis时遇到的问题,大多数问题总结起来就是对MyBatis和Spring框架不熟悉的原因导致的。实际上,在Spring Boot中使用MyBatis本质就是在Spring框架中集成MyBatis,并没有其他任何高级的东西。只不过在Spring Boot中使用时因为插件封装的关系使得相关的配置可以更简洁一些,但是这种封装对于不熟悉MyBatis的人来讲反而增加了理解的难度。因此,我想把如何在Spring Boot中使用MyBatis进行一个系统性的总结,希望能有一些参考价值。
编程随笔
2019/09/11
9.8K0
Spring Boot集成MyBatis的2种方式
MyBatis学习总结(四)——MyBatis缓存与代码生成
缓存可以提高系统性能,可以加快访问速度,减轻服务器压力,带来更好的用户体验。缓存用空间换时间,好的缓存是缓存命中率高的且数据量小的。缓存是一种非常重要的技术。
张果
2018/11/08
1.5K0
MyBatis 极速入门
持久化是将程序数据在持久状态和瞬时状态间转换的机制。即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。持久化的主要应用是将内存中的对象存储在数据库中,或者存储在磁盘文件中、XML数据文件中等等。JDBC就是一种持久化机制。文件IO也是一种持久化机制。
sowhat1412
2020/11/05
3.1K0
MyBatis 极速入门
mybatis原理,配置介绍及源码分析
mybatis是一款优秀的持久层框架,支持定制SQL语句,避免了几乎所有JDBC代码和手动设置参数,结果集获取
kinnylee
2020/10/15
1.3K0
mybatis原理,配置介绍及源码分析
相关推荐
还在用分页?太Low !试试 MyBatis 流式查询,真心强大!
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验