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…

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 ) 二维 用指…

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

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

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

arcgis mxt模板 创建工具条无法保存_【从零开始学GIS】ArcGIS中的绘图基本操作(二)...

大家好&#xff0c;我是肝教程肝到熊猫眼的三三。本系列教程的发布&#xff0c;受到了很多同学的鼓励&#xff0c;大家在后台或微信上表达出对教程的喜爱&#xff0c;这便是更新教程的最大动力。上回教程讲解了“GIS基本操作”、“创建文档&#xff06;加载数据”、“创建GIS数…

vivado中交织模块_搞定Markdown中的图片,一劳永逸的方法!

经常用markdown写博客的朋友一定都体会过markdown图片的蛋疼之处&#xff0c;并不是说图片的这用引用方式不好&#xff0c;而且图片要放到什么服务器上&#xff1f;以我个人为例&#xff0c;写了一篇markdown&#xff0c;想在不修改任何地方的同时适用于各种平台。刚开始使用ma…

delphi xe2 project菜单怎么没有加组件功能_交互设计:让人困惑的三大交互组件及用法...

本篇文章中&#xff0c;笔者带我们了解了三大交互组件专有名词的用法&#xff0c;并结合实际案例进行了解释说明&#xff0c;与大家分享。希望通过此文能够加深你对交互组件及其用法的理解和分析。最近有很多同学来问一些设计中组件的专业名词&#xff0c;发现大家好像都没有一…

北斗三号b1c频点带宽_北斗三号导航信号的创新设计(一)

《测绘学报》构建与学术的桥梁 拉近与权威的距离一需求与挑战1.1 卫星导航信号的作用与意义卫星导航信号是由导航卫星向地球表面播发的无线电测量信号&#xff0c;承担着传递时空基准信息和实施被动测距两大任务&#xff0c;是卫星导航系统实现三边定位的核心载体。卫星导航信号…

g++ linux 编译开栈_linux gcc和g++版本的修改

##编译的时候容易遇到&#xff1a;unsupported GNU version! gcc versions later than 6 are not supported这样的错误。所以我们要更改系统的gcc和g编译器版本。首先我们要知道一个基础的常识&#xff0c;一般而言&#xff0c;linux系统会把gcc和g默认安装在/usr/bin/的目录下…

Linux I2C核心、总线与设备驱动(一)

本章导读 I2C总线仅仅使用SCL、SDA两根信号线就实现了设备之间的数据交互&#xff0c;极大地简化对硬件资源和PCB板布线空间的占用。因此&#xff0c;I2C总线被非常广泛地应用在EEPROM、实时钟、小型LCD等设备与CPU的接口中。 Linux定义了系统的I2C驱动体系结构&#xff0c;在L…

ebay注册流程_跨境电商平台eBay企业入驻流程

整个流程一共包括8个部分了解企业入驻通道企业入驻通道将帮助现有eBay大中华卖家申请并获得高刊登额度的新账户&#xff0c;以满足卖家在品类拓展以及站点拓展的需求。准备材料1.营业执照2.法人代表身份证明&#xff0c;或eBay帐户注册人身份证明&#xff08;根据地区法规有不同…

Linux I2C核心、总线与设备驱动(二)

从上面的分析可知&#xff0c;虽然I2C硬件体系结构比较简单&#xff0c;但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板&#xff0c;面对复杂的 Linux I2C子系统&#xff0c;应该如何下手写驱动呢&#xff1f;究竟有哪些是需要亲自做的&#xff0c;哪些是…

mtu设置失败_Oracle RAC该调整网卡MTU值

在Oracle RAC的环境中&#xff0c;如果我们发现OSW监控数据显示包重组失败率过高&#xff0c;就需要引起足够的重视&#xff0c;因为这很可能会引发member kill/Node kill等重大故障&#xff0c;甚至在有些场景会连带影响到所有RAC节点不可用。一般我们会选择调整ipfrag相关参数…

判断字段长度大于某长度_判断数据库性能只能通过count(*)?No,这些优化方案了解一下!...

大多数用户在体验数据库时&#xff0c;接触到的最早的sql语句就是count(*)&#xff0c;因此用户判断数据库性能时通常也会通过count(*)进行比较。但在执行时通常会出现一个问题&#xff1a;对某个表做count(*)时需对全表数据进行扫描&#xff0c;当表中包含数据量较大的字段时&…

10 款基于 jQuery 的切换效果插件推荐

本文整理了 10 款非常好用的jQuery切换效果插件&#xff0c;包括平滑切换和重叠动画等&#xff0c;这些插件可以实现不同元素之间的动态切换。 1. InnerFade 这是一个基于jQuery的小插件&#xff0c;可以实现页面内的元素淡入淡出效果。 源码/演示 2. HighlightFade 该插件可以…

js矢量图类库:Raphaël—JavaScript Library

官方网址&#xff1a;http://raphaeljs.com/ Raphal is a small JavaScript library that should simplify your work with vector graphics on the web. If you want to create your own specific chart or image crop and rotate widget, for example, you can achieve it si…