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 取数据非常简单:

try(Cursor cursor = mapper.querySomeData()) {
    cursor.forEach(rowObject -> {
        // ...
    });
}

使用 try-resource 方式可以令 Cursor 自动关闭。
但构建 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流式查询的实现方法的文章就介绍到这了,更多相关MyBatis流式查询内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • MyBatis如何实现流式查询的示例代码

    基本概念 流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果.流式查询的好处是能够降低内存使用. 如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询.因此流式查询是一个数据库访问框架必须具备的功能. 流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自

  • MyBatis流式查询的三种实现方法

    导读:流式查询指的是查询成功后不是返回一个集合而是返回一个迭代器,应用每次从迭代器取一条查询结果.流式查询的好处是能够降低内存使用 如果没有流式查询,我们想要从数据库取 1000 万条记录而又没有足够的内存时,就不得不分页查询,而分页查询效率取决于表设计,如果设计的不好,就无法执行高效的分页查询.因此流式查询是一个数据库访问框架必须具备的功能. 流式查询的过程当中,数据库连接是保持打开状态的,因此要注意的是:执行一个流式查询后,数据库访问框架就不负责关闭数据库连接了,需要应用在取完数据后自己关闭

  • MyBatis流式查询的使用详解

    目录 1.应用场景说明 2.模拟excel导出场景 1.创建海量数据的sql脚本 2.MyBatis流式查询 3.Excel通用导出工具类 1.Excel导入导出工具类 2.Excel数据读取监听器 3.Excel读取数据完成回调接口 4.拆分List集合工具类 4.测试结果 5.遗留问题待处理 1.应用场景说明 MyBatis preview: JDBC三种读取方式: 1.一次全部(默认):一次获取全部. 2.流式:多次获取,一次一行. 3.游标:多次获取,一次多行. 在开发中我们经常需要会遇

  • MyBatis流式查询的项目实践

    目录 1.应用场景说明 MyBatis 2.模拟excel导出场景 1.创建海量数据的sql脚本 2.MyBatis流式查询 3.Excel通用导出工具类 4.测试结果 5.遗留问题,待处理 1.应用场景说明 MyBatis preview: JDBC三种读取方式:1.一次全部(默认):一次获取全部.2.流式:多次获取,一次一行.3.游标:多次获取,一次多行. 在开发中我们经常需要会遇到统计数据,将数据导出到excel表格中.由于生成报表逻辑要从数据库读取大量数据并在内存中加工处理后再生成Exc

  • 详解mybatis流式查询与分页插件

    目录 1.流式查询 1.实体类 2.mapper 3.mapper配置 4.自定义处理结果集 5.service层 2.分页插件 1.引入依赖 2.配置文件 3.mapper接口 4.mapper配置 5.servlce层 1.流式查询 1.实体类 package com.wanqi.pojo; import java.util.Date; /** * @Description TODO * @Version 1.0.0 * @Date 2022/9/12 * @Author wandaren

  • jdbc和mybatis的流式查询使用方法

    目录 导语: jdbc流式查询: mybatis流式查询: 导语: 有些时候我们所需要查询的数据量比较大,但是jvm内存又是有限制的,数据量过大会导致内存溢出.这个时候就可以使用流式查询,数据一条条的返回,处理完一条在拿下一条数据,这样每次在内存里面的数据其实很小,不会导致内存溢出. 本文里面会讲到jdbc的流式查询和mybatis的流式查询. jdbc流式查询: jdbc的流式查询需要在生成PreparedStatement的时候设置三个参数.如下: PreparedStatement stm

  • 扩展tk.mybatis的流式查询功能实现

    mybatis查询默认是一次获取全部, 有时候需要查询上万上百万数据时,如果一次性读取到内存中,会容易导致OOM问题.这时候需要采用流式查询.以下扩展了tk.mybatis的流式查询功能. 直接上干货: @Options注解是关键 import org.apache.ibatis.annotations.Options; import org.apache.ibatis.annotations.SelectProvider; import org.apache.ibatis.mapping.Re

  • Mybatis Plus中的流式查询案例

    目录 Mybatis Plus流式查询 通用流式查询 Mybatis Plus大数据量流式查询 Mybatis Plus流式查询 mybatis plus 中自定义如下接口,就可以实现流式查询,mybatis 中同样适用. @Select("select * from t_xxx t ${ew.customSqlSegment}") @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000) @Resul

  • MySQL中的流式查询及游标查询方式

    目录 一.业务场景 二.罗列一下三种处理方式 2.1 常规查询 2.2 流式查询 2.3 游标查询 三.RowData 3.1 RowDataStatic 3.2 RowDataDynamic 3.3 RowDataCursor 四.JDBC 通信原理 4.1 generalQuery 普通查询 4.2 streamQuery 流式查询 4.3 cursorQuery 游标查询 五.并发场景 六.总结 一.业务场景 现在业务系统需要从 MySQL 数据库里读取 500w 数据行进行处理 迁移数据

  • spring与mybatis三种整合方法

    1.采用MapperScannerConfigurer,它将会查找类路径下的映射器并自动将它们创建成MapperFactoryBean. spring-mybatis.xml: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3

随机推荐