Java Mybatis
Mybatis查询数据库中大量的数据,程序抛出:

  1. java.lang.OutOfMemoryError: Java heap space

看下日志,是因为一次查询数据量过大导致JVM内存溢出了,虽然可以配置JVM大小,但是指标不治本,还是需要优化代码。网上查看大家都是流式查询,这里记录下解决的过程。

1、Mapper.xml配置

select语句需要增加fetchSize属性,底层是调用jdbc的setFetchSize方法,查询时从结果集里面每次取设置的行数,循环去取,直到取完。默认size是0,也就是默认会一次性把结果集的数据全部取出来,当结果集数据量很大时就容易造成内存溢出。

  1. <select id="listTaskResultIpInfo" fetchSize="1000" resultType="String">
  2. select info from task_result_info where project_id = #{projectId} and task_id = #{taskId}
  3. </select>

:::danger 注意:此时需要在mysql连接URL中增加useCursorFetch=true。 :::

  1. jdbc.url=jdbc:mysql://127.0.0.1:3306/test?useCursorFetch=true

2、自定义ResultHandler

  1. package com.fcant.test.handler.result;
  2. import com.iie.test.entity.po.custom.CustTaskResultInfo;
  3. import org.apache.ibatis.session.ResultContext;
  4. import org.apache.ibatis.session.ResultHandler;
  5. import java.util.ArrayList;
  6. import java.util.List;
  7. /**
  8. * MyBatis中使用流式查询避免数据量过大导致OOM
  9. */
  10. public class ResultInfoHandler implements ResultHandler<CustTaskResultInfo> {
  11. // 存储每批数据的临时容器
  12. private List<CustTaskResultInfo> resultInfoList = new ArrayList<>();
  13. public List<CustTaskResultInfo> getResultInfoList() {
  14. return resultInfoList;
  15. }
  16. @Override
  17. public void handleResult(ResultContext<? extends CustTaskResultInfo> resultContext) {
  18. CustTaskResultInfo custTaskResultInfo = resultContext.getResultObject();
  19. resultInfoList.add(CustTaskResultInfo);
  20. }
  21. }

3、Spring中配置sqlSessionTemplate

  1. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  2. <property name="dataSource" ref="dataSource"/>
  3. <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
  4. <!-- mapper扫描 -->
  5. <property name="mapperLocations" value="classpath:mybatis/mapper/*/*.xml"/>
  6. </bean>
  7. <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
  8. <constructor-arg index="0" ref="sqlSessionFactory" />
  9. </bean>

4、service中使用

  1. @Autowired
  2. private SqlSessionTemplate sqlSessionTemplate;
  3. public List<CustTaskResultInfo> listTaskResultInfo(Long projectId, Long taskId) {
  4. Map<String, Object> param = new HashMap<>();
  5. param.put("projectId", projectId);
  6. param.put("taskId", taskId);
  7. ResultInfoHandler handler = new ResultInfoHandler();
  8. sqlSessionTemplate.select("com.iie.cyberpecker.dao.custom.CustTaskResultInfoMapper.listTaskResultInfo", param, handler);
  9. return handler.getResultInfoList();
  10. }

上面这种方案必须要定义一个sqlSessionTemplate。