Mybatis 流式查询
简介:流式查询指的是数据库查询成功后,返回一个迭代器,而不是返回一个集合,每次从迭代器取一条查询结果放到缓存器中,当缓存器满了之后,放到结果集中,清空缓存,按游标位置继续读取数据。流式查询的好处是能够降低内存使用,防止java内存溢出(OOM,全称OUT OF MEMORY)。
原理
和StringBuffer的原理相似,Mybatis 流式查询是逐条将读取的数据放到resultset结果集中
流程
- client发送select查询请求给Server
- Server根据条件筛选符合条件的记录,然后就会把记录发送到buffer
- buffer满了就flush缓存(这里要注意的是如果client的buffer缓存满了,那么Server的发送就会阻塞,直到client的buffer缓存空闲。),通过网络发送到client的buffer缓存
- 当不用游标时候(缓存满了的时候),MySqI就会从buffer缓存里面逐个读取记录到resultset。
就这样client 从自己的buffer缓存读取数据到resultset,同时Server端不断通过网络向client的buffer缓存发送数据,直到所有记录都放到了resultset。(操作期间数据库一定要持续连接,操作完成后必须断开数据库连接,减少资源浪费)
关联的接口
MyBatis提供了一个叫 org.apache.ibatis.cursor.Cursor 的接口类用于流式查询,这个接口继承了 java.io.Closeable 和 java.lang.Iterable 接口,由此可知:
- Cursor 是可关闭的
- Cursor 是可遍历的
@Mapper
public interface TestMapper {
@Select("select * from test")
Cursor scan();
}
从以上代码可知返回值为Cursor,但是需要注意的是,每此对数据库进行操作的时候,数据库就会关闭,这就会导致报错,原因是因为Cursor必须持续对数据库进行操作,而且操作完成之后还需要对数据库进行关闭
以下为报错信息:
java.lang.IllegalStateException: A Cursor is already closed.
流式查询报错解决方式
在方法上添加@Transactional 注解(注解可加在Controller、ServiceImpl的方法上)
@GetMapping("test/scan")
@Transactional
public void scanTest() throws Exception {
try {
Cursor cursor = testMapper.scan();
cursor.forEach(data -> { });
}
}
需要注意的是,@Transactional 注解使用的方法必须为对外的接口,而不是内部调用的方法,否则依旧会报错。