流式查询
在我们查询大数据量数据,而内存不够的时候,我们常常使用的做法就是分页,分页的效率往往是低下的,当然如果表有自增id主键,基于自增id主键去查询效率也还行,但这不是我们今天介绍的重点,我们今天介绍的重点是Mybatis流式查询,也就是我们在查询数据的时候查询成功后返回的结果集不是一个集合而是一个迭代器,每次从迭代器中处理查询一条结果,这样就能避免大数据导致的OOM。
实战
MyBatis 流式查询的核心接口是 Cursor
可以看到 Cursor 继承了 Iterable接口。由Iterable知Cursor 是可关闭和遍历的。其中Cursor 的核心方法
//于在取数据之前判断 Cursor 对象是否是打开状态。只有当打开时 Cursor 才能取数据
boolean isOpen();
//用于判断查询结果是否全部取完
boolean isConsumed();
//返回已经获取了多少条数据
int getCurrentIndex();
使用非常简单,有如下几种方式使用,我们来看看
使用流式查询不能简单的直接执行Mapper,因为传统执行Mapper方法的结果是Mapper方法执行完连接就关闭了,导致异常,所以需要我们自己手动处理
方法一
自己通过 SqlSession 获取Mapper类
mapper.xml
<select id="testBatch" resultType="com.sinoxk.order.model.OrderDetail">
SELECT *
from
order_detail
</select>
接口
mapper.class
Cursor<OrderDetail> testBatch();
这里需要注意返回的结果是Cursor而不是集合
测试
@Autowired
@Qualifier("clickHouseSqlSessionFactoryBean")
SqlSessionFactory sqlSessionFactory;
@Test
public void testSelectBatch() throws Exception {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Cursor<OrderDetail> orderDetails = sqlSession.getMapper(CKOrderDetailMapper.class).testBatch();
for (OrderDetail orderDetail : orderDetails) {
System.out.println(orderDetail.getDetailId());
}
} catch (Exception e) {
throw new Exception(e);
}
}
这里我因为使用了多数据源所以需要使用@Qualifier指定注入的SqlSession,单数据源可以忽略
这里我测试的数据量是3w+,我将JVM内存调到64m运行
JVM参数
-Xms64m
-Xmx64m
打开 Java VisualVM对JVM进行监控
发现即使3w+的数据也没有超出内存
而使用普通的查询直接报错OOM
方法二
用 TransactionTemplate 来执行一个数据库事务,保证连接一直是打开的。
@Resource
private PlatformTransactionManager transactionManager;
@Test
public void test23232() {
TransactionTemplate transactionTemplate =
new TransactionTemplate(transactionManager); // 1
transactionTemplate.execute(status -> { // 2
try (Cursor<OrderDetail> orderDetails = ckOrderDetailMapper.testBatch()) {
orderDetails.forEach(orderDetail -> { });
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
}
方法三
也是和方法二类似,方法二是编程式事务,方法三是声明式事务,这里需要注意事务的失效问题
@Transactional
public void scanFoo3(@PathVariable("limit") int limit) throws Exception {
try (Cursor<OrderDetail> orderDetails = ckOrderDetailMapper.testBatch()) {
orderDetails.forEach(foo -> { });
}
}