Mybatis 的流式查询

流式查询指的是查询结果是一个迭代器,而不是一个集合,应用每次从迭代器查询一条结果。流式查询的好处是降低内存的使用。
但是要注意流式查询的过程中要保持连接一直存在,所以流式查询以后数据库访问框架就不负责关闭数据库连接了,需要自己去维护。

流式查询接口

package org.apache.ibatis.cursor;

import java.io.Closeable;

/**
 * Cursor contract to handle fetching items lazily using an Iterator.
 * Cursors are a perfect fit to handle millions of items queries that would not normally fits in memory.
 * If you use collections in resultMaps then cursor SQL queries must be ordered (resultOrdered="true")
 * using the id columns of the resultMap.
 *
 * @author Guillaume Darmont / guillaume@dropinocean.com
 */
public interface Cursor<T> extends Closeable, Iterable<T> {

  /**
   * @return true if the cursor has started to fetch items from database.
   */
  boolean isOpen();

  /**
   *
   * @return true if the cursor is fully consumed and has returned all elements matching the query.
   */
  boolean isConsumed();

  /**
   * Get the current item index. The first item has the index 0.
   * @return -1 if the first cursor item has not been retrieved. The index of the current item retrieved.
   */
  int getCurrentIndex();
}

mybatis接口流式查询提供Cursor接口,由此可知:

  1. Cursor 是可以关闭的。实际上Cursor关闭时,数据库连接也一并关闭了。
  2. Cursor是可遍历的
---------------------------------------------mapper-----------------------------------------------
public interface TestMapper {

    Cursor<User> getUserList();
}
---------------------------------------------service-----------------------------------------------
	 @Autowired
	private TestMapper testMapper;

    public void demoService(){
        Cursor<User> cursor = testMapper.getUserList();
        for (User user : cursor) {
            
        }
    }

在这里插入图片描述
还有一种写法

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

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

流式查询构建方式

上面那段代码,构建mapper方法,返回类型是Cursor, @Autowired自动注入mapper,然后遍历结果,逻辑上是没有问题的。当我们将mapper返回参数定为Cursor,Mybatis就明白这是一个流式查询。这么些语法上没有任何问题,但是运行会报异常。

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

原因:Mapper执行完成之后,连接就关闭了,所以Cursor也就一并关闭了。
解决问题的核心就是让数据库的连接保持
参考网上的文章有3种方式:

  • 方法一:SqlSessionFactory

用SqlSessionFactory手动打开连接,代码修改如下

    @Autowired
    private SqlSessionFactory sqlSessionFactory;

    public void demoService() throws Exception{
        try(
                SqlSession sqlSession = sqlSessionFactory.openSession();
                Cursor<User> userList = sqlSession.getMapper(TestMapper.class).getUserList();
                ) {
                userList.forEach(listItem->{

                });
        }
    }

这次需要我们手动关闭session连接

  • 方法二:TransactionTemplate

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

    @Autowired
    private TestMapper testMapper;

    @Autowired
    private PlatformTransactionManager platformTransactionManager;

    public void demoService() throws Exception{
        TransactionTemplate template = new TransactionTemplate( platformTransactionManager);
        List<User> list = template.execute( status -> {
            try (
                    Cursor<User> userList = testMapper.getUserList();
            ) {
                List<User> users = new ArrayList<>();
                userList.forEach(item->{
                    users.add(item);
                });
                return users;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        });
    }
  • 方法三:@Transactional 注解
    @Transactional
    public void demoService() throws Exception{
        try (
                Cursor<User> userList = testMapper.getUserList();
        ) {
            List<User> users = new ArrayList<>();
            userList.forEach(item->{
                users.add(item);
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

使用注解注意,这个和异步执行标识@async一样,同一个类中方法调用是不起作用的,就是和demoService在同一个类中的另一个方法调用了这个方法, @Transactional注解不起作用

Mybatis - plus 的实现方式

-----------------------------------------------------------------mapper-----------------------------------------------
    @Select("${sql}")
    //这个注解是设定每次流式查询的iterator大小的,这里是1000条
    //ResultSetType.FORWARD_ONLY 只允许游标向下移动
    @Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
    //这个注解是标明返回结果集类型的
    @ResultType(Map.class)
    void dynamicSelectLargeData(@Param("sql") String sql, ResultHandler<Map> handler);

-------------------------------------------------------------------service-----------------------------------------------

    public void dynamicSelectLargeData(String sql) {
    	//sql是查询的sql语句,resultContext是ResultHandler类型,也就是mapper查询结果,这种方式不需要注解和自己创建session
        dynamicMapper.dynamicSelectLargeData(sql,
                resultContext -> {
                    Map one = (Map) resultContext.getResultObject();
                    //接着处理流式查询结果
                    System.out.println(one);
                });
    }

参考01

  • 6
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值