大量数据导出execl

大量数据导出execl

背景

最近有个现网问题,管理系统导出大量数据时候,出现了oom。分析代码需要优化的地方:

  1. 使用hibernate查询大量数据,会生成大量对象,最终导致oom,此处需要分批查询。

  2. 使用工具包 :poi-ooxml。SXSSFWorkbook能够在内存达到_randomAccessWindowSize会自动刷到磁盘,但是代码中先在内存中创建全量的row然后再往SXSSFWorkbook中写,依然会耗尽内存。此处需要分批往SXSSFWorkbook中写。
    下面是SXSSFSheet的createRow方法:createRow

  3. 如果我们使用多线程分批处理,SXSSFSheet不支持多线程写,它只会生成一个临时文件,SXSSFWorkbook的分页可以支持并发写,这又不符合需求。因此这里我改写SXSSFWorkbook源码,为每个线程分别创建一个临时文件,最后导出的时候再将多个文件流合并成一个文件流。

代码

  1. 导入pom:
       <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
        </dependency>
  1. 多线程处理:
 public void exportOrderExcelData(Map<String, String> params, OrderExcelView orderExcelView) throws ExecutionException, InterruptedException, YYZXException {
        List<Object> paramValues = new ArrayList<Object>();
        String hql = "";
        if (StringUtils.isNotBlank(params.get("estimationFrom"))) {
            hql = OrderQuery.parseQueryByEstimationFrom(params, paramValues);
        } else {
            hql = parseQueryCondition(params, paramValues);
        }
        String countHql = platformReadOnlyDao.toCountHQL(hql);
        Integer countNums = platformReadOnlyDao.getCommonDao().findCount(countHql, paramValues.toArray());
        if (countNums > 2000000) {
            throw new YYZXException(RetCode.ORDER_NUM_OVER_ERROR.getCode(), RetCode.ORDER_NUM_OVER_ERROR.getDesc());
        }
        List<SujiOrganizationConfig> sujiConfigList = platformReadOnlyDao.find("from SujiOrganizationConfig");
        if (countNums < max_send) {
            List<Object> datas = platformReadOnlyDao.find(hql, paramValues.toArray());
            ObjectStreamToSXSSFWorkbook.objectToSXSSFWorkbook(orderExcelView, getOrderRowLists(rebuildDatas(datas), sujiConfigList), 0, max_send,countNums);
        } else {
            int limit = countStep(countNums);
            CountDownLatch countDownLatch = new CountDownLatch(limit);
            for (int i = 0; i < limit; i++) {
                int finalI = i;
                String finalHql = hql;
                CompletableFuture.runAsync(() -> {
                    try {
                        List<Object> datas = platformReadOnlyDao.findWithLimit(finalHql, finalI * max_send, max_send, paramValues.toArray());
                        ObjectStreamToSXSSFWorkbook.objectToSXSSFWorkbook(orderExcelView, getOrderRowLists(rebuildDatas(datas), sujiConfigList), finalI, max_send, countNums);
                    } finally {
                        countDownLatch.countDown();
                    }
                }, executor);
            }
            countDownLatch.await();
        }

    }
    /**
     * 计算切分次数
     */
    private Integer countStep(Integer size) {
        return (size + max_send - 1) / max_send;
    }
  1. 修改SXSSFWorkbook、SXSSFSheet部分源码:
    SXSSFSheet修改
    在SXSSFSheet类中增加_writerMap和_rowsMap键为线程index。为每个线程创建独立的_rows和_writer。
    SXSSFWorkbook修改
    修改SXSSFWorkbook类的copyStreamAndInjectWorksheet方法,使之支持多个文件流copy.

总结

日常开发中应该多阅读源码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值