mybatis删除成功返回0_你还在用分页?试试 MyBatis 流式查询,真心强大!

f7f3ee0d3bae326ac4178a2133301fef.png

转自:捏造的信仰

segmentfault.com/a/1190000022478915

基本概念

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

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

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

MyBatis 流式查询接口

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

  1. Cursor 是可关闭的;
  2. Cursor 是可遍历的。

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

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

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

cursor.forEach(rowObject -> {...});

但构建 Cursor 的过程不简单

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

@Mapper
public interface FooMapper {
    @Select("select * from foo limit #{limit}")
    Cursor scan(@Param("limit") int limit);
}

方法 scan() 是一个非常简单的查询。通过指定 Mapper 方法的返回值为 Cursor 类型,MyBatis就知道这个查询方法一个流式查询。

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

@GetMapping("foo/scan/0/{limit}")
public void scanFoo0(@PathVariable("limit") int limit) throws Exception {
    try (Cursor cursor = fooMapper.scan(limit)) {  // 1
        cursor.forEach(foo -> {});                      // 2
    }
}

上面的代码中,fooMapper 是 @Autowired 进来的。注释 1 处调用 scan 方法,得到 Cursor 对象并保证它能最后关闭;2 处则是从 cursor 中取数据。

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

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

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

所以,解决这个问题的思路不复杂,保持数据库连接打开即可。我们至少有三种方案可选。关注公众号Java技术栈获取 Mybatis 及更多面试题带答案。

方案一:SqlSessionFactory

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

@GetMapping("foo/scan/1/{limit}")
public void scanFoo1(@PathVariable("limit") int limit) throws Exception {
    try (
        SqlSession sqlSession = sqlSessionFactory.openSession();  // 1
        Cursor 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 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 cursor = fooMapper.scan(limit)) {
        cursor.forEach(foo -> { });
    }
}

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

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

- END -

版权声明:“程序员识堂”所推送文章及配图非商业用途。若涉及版权问题,烦请原作者联系删除处理,谢谢!

- end -

4c478ac55e07f93f7e7f723295e36645.png5f28e002db40cdf3e495938f2a1e3f93.gif扫码关注我们5f28e002db40cdf3e495938f2a1e3f93.gif

点个在看,就是对我最大的支持

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/434773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C++程序设计--第三章内容

提前声明: 本文内容为华北水利水电大学研究生C课程,如有 侵权请告知,作者会予以删除 1.函数 函数作用 —— 任务划分;代码重用定义形式 类型 函数名 ( 形式参数表){语句序列}调用形式 函数名&#x…

PHP验证变量类型

isset() : //变量是否已经声明empty(): //变量是否为空defined(): //常量是否已经定义 de…

Linux TCP server系列(5)-select模式下的单进程server

目标: 让服务器退化为单进程模式,但是利用select来提升性能 思路: (1)服务器 传统的单进程服务器一旦accept了客户端的TCP连接后,就转入客户请求的处理,处理完成后才能再一次的调用…

替换元素_80%的前端会答错的问题:lt;imggt;是什么元素?

前言某天晚上&#xff0c;和几个朋友去撸串&#xff0c;突然就聊到了面试&#xff0c;都在感叹现在的面试题太变态了&#xff0c;其中一个突然很神秘的问我&#xff1a;“你写前端这么久了&#xff0c;那你知道 <img> 是什么元素吗&#xff1f;”于是我结合平时写页面的经…

用指针变量访问数组

一维指针 地址值 a 相当于 & a[ 0 ] a 1 相当于 & a[ 1 ] a 2 相当于 & a[ 2 ] a i 相当于 & a[ i ] 元素值 a [ 0 ] * a a [ 1 ] 相当于 * ( a 1 ) a [ 2 ] 相当于 * ( a 2 ) a [ i ] 相当于 * ( a i ) 二维 用指…

LEX和YACC的使用(例子)

1、简单C语言的词法分析程序;%{#include<stdio.h>#include<stdlib.h>#include<ctype.h>#include<string.h> %} digit [0-9]letter [A-Za-z]other_char [!-\[-~]id ({letter}|[_])({letter}|{digit}|[_])*string …

C++排序法

选择排序 // sort void sort ( int x[] , int n ) { int max , t ;for ( int i 0 ; i < n-1 ; i ) //对数组排序{ t i ;for ( int j i 1 ; j < n ; j ) //寻找最大元素if ( x [ j ] > x [ t ] ) t j ;if ( t ! i ){ max x [ i ] ; //交换…

Linux TCP server系列(6)-select模式下的多线程server

目标&#xff1a; 修改上一篇的select模式下的server&#xff0c;让它使用多线程来处理客户端请求&#xff08;多进程的模式已经在上篇中加了注释&#xff09;。 思路&#xff1a; &#xff08;1&#xff09;服务器 我们已经在之前的客户端模型多个并发用户的过程中使用过多线程…

单选按钮_PerlTk教程之按钮Button、复选按钮Checkbutton、单选按钮Radiobutton(附完整代码)...

《Perl-Tk教程之按钮Button、复选按钮Checkbutton、单选按钮Radiobutton》Perl-Tk中有三种不同形式的按钮组件可供选择&#xff0c;它们分别是按钮(Button), 复选按钮(Checkbutton), 和单选按钮(Radiobutton)&#xff0c;如下图所示&#xff1a;这三种按钮看起来是不同的&#…

致读者

有很多理由说明你应该读这个手册&#xff0c;然而也有这样的疑问&#xff1a;为什么要舍近求远&#xff0c;而不是下载一个存在的发行版&#xff1f; 一个很重要的理由是这可以帮助你里里外外的更加了解Linux的工作原理。 另一个关键因素是你可以更多的控制你的系统&#xff0…

制作.ppm格式Linux内核启动logo

在做的产品中&#xff0c;采用默认的小企鹅做logo肯定是不行的&#xff0c;所以一定要替换个像样的企业logo图片&#xff01; 以前做过Linux启动Logo&#xff0c;时间太久&#xff0c;给忘了&#xff0c;所以再从新研究下。 经过在网上的搜索&#xff0c;和实际验证&#xff0c…

在恰当的地方使用MongoDB的WriteConcern.SAFE参数

首先列一下WriteConcern的几种抛出异常的级别参数&#xff1a; WriteConcern.NONE:没有异常抛出WriteConcern.NORMAL:仅抛出网络错误异常&#xff0c;没有服务器错误异常WriteConcern.SAFE:抛出网络错误异常、服务器错误异常&#xff1b;并等待服务器完成写操作。WriteConcern.…

好奇怪呀后面加什么标点_狗狗吃饭时奇怪的小动作,你知道代表什么吗?做个懂狗的好主人...

狗狗有时候因为一些奇怪的小行为&#xff0c;会让主人觉得很可爱。如果我们希望能够了解狗狗更多一些&#xff0c;那么我们需要透过它们的行为本身&#xff0c;去理解背后所代表的含义&#xff0c;才能和狗狗更亲密的交流。很多狗狗在吃饭的时候&#xff0c;也会表现出一些奇奇…

关闭TCP连接的学问

从TCP协议角度来看&#xff0c;一个已建立的TCP连接有两种关闭方式&#xff0c;一种是正常关闭&#xff0c;即四次挥手关闭连接&#xff1b;还有一种则是异常关闭&#xff0c;我们通常称之为连接重置&#xff08;RESET)。 首先说一下正常关闭时四次挥手的状态变迁&#xff0c;关…

where 1=1低效?

最近一直有点纠结于sql语句里面的where 11是不是低效的&#xff1f;有人跟我说不会降低sql的效率&#xff0c;但是我理解这里应该是低效的。有一些是说where 11会造成数据库表的优化失效&#xff0c;比如聚集索引的什么&#xff0c;具体我也不清楚里面的原理是怎样的。 我这样觉…

开机未发现nvidia控制面板_修改这几个选项,就能提升你的开机速度

最近电脑非常卡&#xff0c;有时真的想把它给砸了&#xff0c;慢的自己都受不了&#xff0c;开机几分钟&#xff0c;开机完还要等上好久才能运行软件&#xff0c;都快受不了&#xff0c;要不是看在已经是10前的买的电脑&#xff0c;早就问候产商了&#xff0c;电脑缓慢的开机速…

ubuntu下使用UVC摄像头

导师让学习opencv编程&#xff0c;想用Qt但是发现windows上Qt使用directshow非常麻烦&#xff0c;就想到linux上编程&#xff0c;于是买一普通摄像头&#xff0c;回来一看&#xff0c;插在ubuntu电脑上没有反应&#xff0c;知道视驱动的问题&#xff0c;开始探寻如何在ubuntu上…

bearer token_bearer token到底是什么?

在以前&#xff0c;用户进行认证的时候一般是&#xff1a;-> 用户向服务端发送验证信息(用户名、密码)。-> 服务端验证成功就向用户返回一个sessionid&#xff0c;服务端保存了这个session_id对应的信息&#xff0c;写入用户的 Cookie。-> 之后前端发出的每一次请求&a…

一些cron命令

列出当前用户的cron:crontab -l 列出指定用户的cron:crontab -uroot -l 编辑当前用户的cron:crontab -e 删除当前用户的cron:crontab -r cron文件语法: 分 小时 日 月 星期 命令 0-59 0-23 1-31 1-12 0-6 command (取值范围,0表示周…

'ADB server didn't ACK'的解决办法

网上搜了很多种&#xff0c;但是对我来说都不管用。引起这个错误的原因是有很多方面的。 1.软件冲突。 首先是豌豆荚&#xff0c;尝试关闭豌豆荚&#xff0c;重启eclipse后尝试run as android application.... 2.进程里 关闭adb.exe&#xff0c;重启eclipse 3.软件更新 a…