0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

MyBatis流式查询轻松帮你解决分页慢的问题

5jek_harmonyos 来源:思否开发者社区 作者:捏造的信仰 2021-08-04 15:52 次阅读

作者丨捏造的信仰

segmentfault.com/a/1190000022478915

Part1基本概念

流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果。流式查询的好处是能够降低内存使用。

如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询。因此流式查询是一个数据库访问框架必须具备的功能。

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

Part2MyBatis 流式查询接口

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

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

Cursor 是可遍历的。

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

isOpen():用于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据;

isConsumed():用于判断查询结果是否全部取完;

getCurrentIndex():返回已经获取了多少条数据。

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

try(Cursor cursor = mapper.querySomeData()) {

cursor.forEach(rowObject -》 {

// 。。。

});

}

使用 try-resource 方式可以令 Cursor 自动关闭。

Part3但构建 Cursor 的过程不简单

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

@Mapper

public interface FooMapper {

@Select(“select * from foo limit #{limit}”)

Cursor《Foo》 scan(@Param(“limit”) int limit);

}

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

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

@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) 时会报错:

java.lang.IllegalStateException: A Cursor is already closed.

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

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

方案一:SqlSessionFactory

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

@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 来执行一个数据库事务,这个过程中数据库连接同样是打开的。代码如下:

@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 注解

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

@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 流式查询的方法。

编辑:jq

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mybatis
    +关注

    关注

    0

    文章

    58

    浏览量

    6698

原文标题:还在担心分页慢吗? MyBatis 流式查询解决你的烦恼

文章出处:【微信号:harmonyos_developer,微信公众号:harmonyos_developer】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    流式滑动变阻器的选型原则

    流式滑动变阻器,又称为限流电阻器或限流电位器,是一种用于限制电路中电流大小的电子元件。在电子电路设计中,选择合适的限流式滑动变阻器对于确保电路的稳定运行和提高电路性能至关重要。 一、限流式滑动
    的头像 发表于 08-05 14:31 775次阅读

    使用mybatis切片实现数据权限控制

    一、使用方式 数据权限控制需要对查询出的数据进行筛选,对业务入侵最少的方式就是利用mybatis或者数据库连接池的切片对已有业务的sql进行修改。切片逻辑完成后,仅需要在业务中加入少量标记代码
    的头像 发表于 07-09 17:26 334次阅读
    使用<b class='flag-5'>mybatis</b>切片实现数据权限控制

    流式继电器输入的是什么电源

    流式继电器是一种常见的电气元件,广泛应用于电力系统、工业自动化、通信设备等领域。它的核心功能是将输入的交流电源转换为直流电源,以驱动继电器的线圈,实现对电路的控制。本文将详细介绍整流式继电器
    的头像 发表于 06-28 10:21 641次阅读

    流式继电器结构上有哪些特点

    流式继电器是一种利用整流原理来实现继电器动作的电气设备,广泛应用于电力系统、工业自动化、通信设备等领域。本文将详细介绍整流式继电器的结构特点,包括其工作原理、主要组成部分、性能指标、应用场景等方面
    的头像 发表于 06-28 10:15 834次阅读

    分库分表后复杂查询的应对之道:基于DTS实时性ES宽表构建技术实践

    分表,通过分库分表应对存系统读写性能瓶颈和存储瓶颈;分库分表帮我们解决问题的同时,也带来了复杂性;比如多条件的分页查询,多条件的联表查询变得复杂起来,通过调研我们发现针对这些分页,联表
    的头像 发表于 06-25 18:30 824次阅读
    分库分表后复杂<b class='flag-5'>查询</b>的应对之道:基于DTS实时性ES宽表构建技术实践

    让你的年终总结更有格调!讯飞星火AI商务键盘帮你轻松撰写

    亲爱的朋友们,年底了,是不是已经开始忙碌起来准备年终总结了呢?别担心,我们有科大讯飞AI智能键盘D1来帮你轻松撰写! 首先,让我来为大家介绍一下这款神奇的键盘。它拥有讯飞星火认知大模型,这可是我们
    的头像 发表于 01-05 15:46 510次阅读
    让你的年终总结更有格调!讯飞星火AI商务键盘<b class='flag-5'>帮你</b><b class='flag-5'>轻松</b>撰写

    oracle数据库limit怎么用

    在Oracle数据库中,可以使用ROWNUM来实现类似LIMIT的功能。ROWNUM是Oracle数据库提供的一个伪列,它在查询结果集中为每一行分配一个唯一的数字。 要使用ROWNUM进行分页查询
    的头像 发表于 12-06 10:05 2081次阅读

    mybatis映射文件的主要元素及作用

    MyBatis是一种流行的持久层框架,它提供了一种简单的方式来映射关系型数据库和Java对象之间的关联,通过XML配置文件来描述数据库表和Java类之间的映射关系。在MyBatis的映射文件中,包含
    的头像 发表于 12-03 14:56 2795次阅读

    mybatis逻辑分页和物理分页的区别

    MyBatis是一个开源的Java持久层框架,它与其他ORM(对象关系映射)框架相比,具有更加灵活和高性能的特点。MyBatis提供了两种分页方式,即逻辑分页和物理
    的头像 发表于 12-03 14:54 857次阅读

    mybatis中$和井号区别

    MyBatis是一个开源的Java持久层框架,它提供了许多强大的功能用于简化数据库操作。在MyBatis中,我们可以使用两种方式来动态生成SQL语句:$和#。 和#都可以用来替换SQL语句中的参数
    的头像 发表于 12-03 14:53 930次阅读

    mybatis框架的主要作用

    MyBatis框架的主要作用包括以下几个方面。 数据库操作的简化和标准化: MyBatis框架提供了一种简单的方式来执行数据库操作,包括插入、更新、删除和查询等操作。通过使用MyBatis
    的头像 发表于 12-03 14:49 1978次阅读

    mybatis一级缓存和二级缓存的原理

    SqlSession的生命周期中,当SqlSession关闭时,一级缓存也会被清空。 1.2 缓存实现机制 一级缓存采用了基于PerpetualCache的HashMap来实现,使用一个Map对象来保存缓存的数据。当执行相同的查询时,MyBatis会首先寻找一级缓存中是否
    的头像 发表于 12-03 11:55 1083次阅读

    mybatis和mybatisplus的区别

    MyBatisMyBatis Plus是两个非常受欢迎的Java持久层框架。这两个框架在设计和功能上有一些区别,下面我将详细介绍它们之间的差异以及各自的特点。 设计理念与目标: MyBatis是一
    的头像 发表于 12-03 11:53 2486次阅读

    mybatis接口动态代理原理

    MyBatis是一款轻量级的Java持久化框架,它通过XML或注解配置的方式,将数据库操作与SQL语句解耦,提供了一种简单、灵活的数据访问方式。在MyBatis中,使用动态代理技术来实现接口的代理
    的头像 发表于 12-03 11:52 910次阅读

    mybatis的dao能重载吗

    MyBatis的DAO能否重载? 在MyBatis中,DAO是数据访问对象的缩写,用于执行与数据库交互的操作。MyBatis的DAO可以重载,即可以定义多个具有不同参数的相同方法名的方法,以满足
    的头像 发表于 12-03 11:51 1233次阅读