java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Mybatis游标查询数据

Mybatis游标查询大量数据方式

作者:绊脚石0320

这篇文章主要介绍了Mybatis游标查询大量数据方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

Mybatis游标查询大量数据

对大量数据进行处理时,为防止内存泄漏情况发生,所以采用mybatis plus游标方式进行数据查询处理,当查询百万级的数据的时候,使用游标可以节省内存的消耗,不需要一次性取出所有数据,可以进行逐条处理或逐条取出部分批量处理

mapper层

@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = Integer.MIN_VALUE)
@Select("select domain from illegal_domain where icpstatus != #{icpstatus}")
Cursor<IllegalDomain> getDayJobDomain(@Param("icpstatus") Integer icpstatus);

service层 

Cursor<IllegalDomain> domainList = illegalDomainMapper.getDayJobDomain(1);

数据处理

forEach方式

domainList.forEach(illegalDomain -> {
    //处理逻辑,根据业务需求自行完成
    Future<IcpStatusVo> future = checkIcpThreadPool.submit(new IcpCheckThread(illegalDomain.getDomain(), configMap));
    results.add(future);
});

迭代器 

Iterator<IllegalDomain> iter = domainList.iterator();
while (iter.hasNext()) {
    <!--// Fetch next 10 employees-->
    <!--for(int i = 0; i<10 && iter.hasNext(); i++) {-->
    <!--    smallChunk.add(iter.next());-->
    <!--}-->
    
    //处理逻辑,根据业务需求自行完成
    Future<IcpStatusVo> future = checkIcpThreadPool.submit(new IcpCheckThread(illegalDomain.getDomain(), configMap));
    results.add(future);
}

资源释放

使用完毕后,在finally块释放资源,否则游标不关闭也可能会导致内存溢出问题

try{
    //your code
    
} catch (Exception e) {
    log.error(e);
} finally {
    if(null != domainList){
        try {
            domainList.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Mybatis游标使用总结

当查询百万级的数据的时候,查询出所有数据并放入内存中时会发生OOM(OutOfMemoryException),使用游标可以节省内存的消耗,不需要一次性取出所有数据,可以进行逐条处理或逐条取出部分批量处理,在此场景下就可以使用游标的概念来解决这个问题。

什么是游标

游标(Cursor)是处理数据的一种方法,为了查看或者处理结果集中的数据,游标提供了在结果集中一次一行或者多行前进或向后浏览数据的能力。

Demo:

// 第一种
@Options(resultSetType = ResultSetType.FORWARD_ONLY)
@Select("SELECT * FROM department WHERE status = 0")
List<DepartmentEntity> queryDepartmentAll();
 
// 第二种 在Mybatis-3.4.0版本中,不支持@select注解,在3.4.1版本中已经修复:
@Options(resultSetType = ResultSetType.FORWARD_ONLY)
@Select("SELECT * FROM department WHERE status = 0")
Cursor<Employee> cursorQueryDepartmentAll();
@Service
public class DepartmentServiceImpl implements DepartmentService { 
    private static final Logger LOG = LoggerFactory.getLogger(DepartmentServiceImpl.class);
 
    @Autowired
    private DepartmentDao departmentDao;
 
    @Autowired
    private SqlSessionTemplate sqlSessionTemplate; 
    public List<DepartmentDTO> getDepartmentList(DepartmentSearchParam param) {
        Cursor<Object> cursor = null;
        SqlSession sqlSession = null;
        try {
            sqlSession = sqlSessionTemplate.getSqlSessionFactory().openSession();
            cursor = sqlSession.selectCursor(DepartmentDao.class.getName() + ".queryDepartmentAll");
            cursor.forEach(e -> {
                // 处理逻辑
            });
           // 也可以使用迭代器:Iterator<Object> iterator = cursor.iterator();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != cursor) {
                try {
                    cursor.close();
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            }
            if (null != sqlSession) {
                try {
                    sqlSession.close();
                } catch (Exception e) {
                    LOG.error(e.getMessage(), e);
                }
            }
        }
    }
}

注意:游标是可以前后移动的。如果resultSetType = TYPE_SCROLL_INSENSITIVE ,就是设置游标就可以前后移动。

Mybatis为了保证可以前后移动,Mybatis会把之前查询的数据一直保存在内存中。

所以并不能根本解决OOM,所以我们这里需要设置为@Options(resultSetType = ResultSetType.FORWARD_ONLY)(其实默认就是ResultSetType.FORWARD_ONLY)

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文