Mybatis流式查询(查询数据过大的处理)
问题:
在我们的应用场景中,可能会遇到查询返回的数据为上千万条的情况。如果不处理的话,数据太多可能会爆掉内存,而如果用分页的方式接收的话,可能会受数据库结构限制,降低一些效率。
流式查询
因此,所以有了流式查询的概念。流式查询返回的数据不是一个集合、List,而是一个迭代器,我们通过从迭代器中每次取出一条数据,从而对返回的数据进行处理,进而节约内存,而流式查询的这个特性又决定了,在执行流式查询的过程中,与数据库必须保持连接状态。
使用
接口类:org.apache.ibatis.cursor.Cursor
Mapper方法:
@Mapper
public interface TestMapper {
@Select("select * from test limit #{limit}")
Cursor<Test> scan(@Param("limit") int limit);
}
在mapper,我们只需要指定返回值类型为Cursor,就实现了一个流式查询
Controller:
@GetMapping("test/scan/3/{limit}")
@Transactional
public void scanTest3(@PathVariable("limit") int limit) throws Exception {
try (Cursor<> cursor = testMapper.scan(limit)) {
cursor.forEach(test -> { });
}
}
在Controller中,如果我们要使用这个方法,首先要解决数据库持续连接的问题,因为默认mapper方法在执行之后会关闭数据库连接,所以在使用的时候需要加注解 @Transactional 。这样就实现了一个流式查询过程。当然还有其他方法能够维持数据库的持续连接状态,如:
额外方法一:
try (
SqlSession sqlSession = sqlSessionFactory.openSession(); // 1
Cursor<> cursor =
sqlSession.getMapper(TestMapper.class).scan(limit) // 2
) {
cursor.forEach(test -> { });
}
额外方法二:
TransactionTemplate transactionTemplate =
new TransactionTemplate(transactionManager); // 1
transactionTemplate.execute(status -> { // 2
try (Cursor<Test> cursor = testMapper.scan(limit)) {
cursor.forEach(test -> { });
} catch (IOException e) {
e.printStackTrace();
}
return null;
});
后面两者实现起来都没有注解方便,当然在使用的注解的时候,一定要记得,注解只能在外部调用时起作用。