编辑
2023-09-15
遇到的问题
00
请注意,本文编写于 566 天前,最后修改于 563 天前,其中某些信息可能已经过时。

目录

前提
思路
代码
修改前
修改后
优化空间

前提

由于业务要求使用easyexcel无对象导出的方式,通过sql获取list<string,object>数据,同时列名还需要用字段名转成中文,导致for循环比较多,因为单线程执行,数据量又是几千条,每一行的数据又是七八百列,导致效率非常慢

思路

通过拆分list,将list拆成多段后,使用线程池,每个线程执行一个任务,最后把结果合并起来

代码

修改前

问题比较多,也没对异常做很好的处理

java
public void exportSqlScriptExcuteResultExcel(HttpServletResponse response, RecSearchSolutionSqlScriptExcuteResultRequest request) { request.setType(1); IPage<Map<String, Object>> iPage = this.getSqlScriptExcuteResult(request); List<Map<String,Object>> dataMaplist= iPage.getRecords(); //查询当前方案的已选字段 RecSearchSolutionFieldRequest fieldRequest=new RecSearchSolutionFieldRequest(); fieldRequest.setSolutionId(request.getSolutionId()); List<RecSearchSolutionFieldEntity> recSearchSolutionFieldList=iRecSearchSolutionFieldService.getRecSearchSolutionFieldMapByIsDisplay(fieldRequest); Map<String, String> recSearchSolutionFieldMap = recSearchSolutionFieldList.stream().collect(Collectors.toMap(arr -> arr.getField(), arr -> arr.getText(), (v1, v2) -> null, LinkedHashMap::new)); recSearchSolutionFieldMap.values().removeIf(Objects::isNull); //列名的集合 List<List<String>> headList = new ArrayList<>(); //每行数据的集合 List<List<Object>> dataList = new ArrayList<>(); //是否为首次 0为首次 int i = 0; try { for (Map<String, Object> map : dataMaplist) { List<Object> data = new ArrayList<>(); List<String>[] head = new List[]{null}; for (Map.Entry<String, Object> m : map.entrySet()) { if (recSearchSolutionFieldMap!=null){ if (!recSearchSolutionFieldMap.containsKey(m.getKey())){ continue; } } if (m.getValue() instanceof Date){ data.add(DateUtil.format((Date) m.getValue(),"yyyy-MM-dd HH:ss:mm")); }else { data.add(m.getValue()); } //首次遍历就进入 if (i == 0) { int headNum = 0; head[headNum] = new ArrayList<>(); String text=recSearchSolutionFieldMap.get(m.getKey()); if(StrUtil.isBlank(text)){ head[headNum].add(m.getKey()); }else{ head[headNum].add(text); } headList.add(head[headNum]); headNum++; } } i++; dataList.add(data); } String nowDate = DateUtil.format(new Date(),"yyyy_MM_dd_HH_mm_ss"); String fileName = "综合查询导出_" + nowDate; response.setCharacterEncoding("utf-8"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "utf-8")); WriteCellStyle headWriteCellStyle = new WriteCellStyle(); WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short)10); headWriteCellStyle.setWriteFont(headWriteFont); HorizontalCellStyleStrategy horizontalCellStyleStrategy=new HorizontalCellStyleStrategy(); horizontalCellStyleStrategy.setHeadWriteCellStyle(headWriteCellStyle); EasyExcel.write(response.getOutputStream()).registerWriteHandler(horizontalCellStyleStrategy).head(headList).autoCloseStream(Boolean.FALSE).sheet("sheet").doWrite(dataList); } catch (IOException e) { throw new BusinessException(ResultCodeEnum.ERR_0x1000); } }

修改后

先生成head,不用每次都操作判断i,增加对空结果的操作以及生成excel失败的操作、并且超时情况的处理,把复用代码抽取出来,核心逻辑方便操作。

java
public void exportSqlScriptExcuteResultExcel(HttpServletResponse response, RecSearchSolutionSqlScriptExcuteResultRequest request) throws IOException { request.setType(1); IPage<Map<String, Object>> iPage = this.getSqlScriptExcuteResult(request); List<Map<String,Object>> dataMaplist= iPage.getRecords(); //每列的列名的集合 List<List<String>> headList = new ArrayList<>(); //每行数据的集合 List<List<Object>> dataList = new ArrayList<>(); //如果sql结果为空则返回空的excel if (CollectionUtil.isEmpty(dataMaplist)){ this.generateExcel(response, headList, dataList); }else { //查询当前方案的已选字段 RecSearchSolutionFieldRequest fieldRequest = new RecSearchSolutionFieldRequest(); fieldRequest.setSolutionId(request.getSolutionId()); List<RecSearchSolutionFieldEntity> recSearchSolutionFieldList = iRecSearchSolutionFieldService.getRecSearchSolutionFieldMapByIsDisplay(fieldRequest); Map<String, String> recSearchSolutionFieldMap = recSearchSolutionFieldList.stream().collect(Collectors.toMap(arr -> arr.getField(), arr -> arr.getText(), (v1, v2) -> null, LinkedHashMap::new)); recSearchSolutionFieldMap.values().removeIf(Objects::isNull); //先处理把第一个list拿出来处理EexcelHead Map<String, Object> headmap = dataMaplist.get(0); headmap.entrySet().forEach(h -> { List<String>[] head = new List[]{null}; int headNum = 0; head[headNum] = new ArrayList<>(); String text=recSearchSolutionFieldMap.get(h.getKey()); if(StrUtil.isBlank(text)){ head[headNum].add(h.getKey()); }else{ head[headNum].add(text); } headList.add(head[headNum]); headNum++; }); try { // 创建一个有两个线程的线程池 ExecutorService executor = Executors.newFixedThreadPool(2); // 将dataMaplist分成两个子列表 int mid = dataMaplist.size() / 2; List<Map<String, Object>> sublist1 = dataMaplist.subList(0, mid); List<Map<String, Object>> sublist2 = dataMaplist.subList(mid, dataMaplist.size()); // 提交第一个任务给线程池 executor.execute(() -> { // 遍历第一个子列表 for (Map<String, Object> map : sublist1) { this.processMap(map, dataList); } }); // 提交第二个任务给线程池 executor.execute(() -> { // 遍历第二个子列表 for (Map<String, Object> map : sublist2) { this.processMap(map, dataList); } }); // 关闭线程池 executor.shutdown(); // 等待所有任务完成,如果超出30分钟就抛出异常 executor.awaitTermination(30, TimeUnit.MINUTES); } catch (InterruptedException e) { e.printStackTrace(); // 重置response response.reset(); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); Map<String, String> map = MapUtils.newHashMap(); map.put("status", "failure"); map.put("message", "下载文件失败" + e.getMessage()); response.getWriter().println(JSON.toJSONString(map)); } //生成excel文件 this.generateExcel(response, headList, dataList); } } private void generateExcel(HttpServletResponse response,List<List<String>> headList, List<List<Object>> dataList) throws IOException { try { String nowDate = DateUtil.format(new Date(), "yyyy_MM_dd_HH_mm_ss"); String fileName = "综合查询导出_" + nowDate; response.setCharacterEncoding("utf-8"); response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(fileName + ".xlsx", "utf-8")); WriteCellStyle headWriteCellStyle = new WriteCellStyle(); WriteFont headWriteFont = new WriteFont(); headWriteFont.setFontHeightInPoints((short) 10); headWriteCellStyle.setWriteFont(headWriteFont); HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(); horizontalCellStyleStrategy.setHeadWriteCellStyle(headWriteCellStyle); EasyExcel.write(response.getOutputStream()).registerWriteHandler(horizontalCellStyleStrategy).head(headList).autoCloseStream(Boolean.FALSE).sheet("sheet").doWrite(dataList); } catch (IOException e) { e.printStackTrace(); // 重置response response.reset(); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); Map<String, String> map = MapUtils.newHashMap(); map.put("status", "failure"); map.put("message", "下载文件失败" + e.getMessage()); response.getWriter().println(JSON.toJSONString(map)); } } private void processMap(Map<String, Object> map, List<List<Object>> dataList) { List<Object> data = new ArrayList<>(); map.entrySet().forEach(m-> { data.add(m.getValue()); }); dataList.add(data); }

优化空间

目前感觉还不是太好还有进一步优化空间,比如说通过Runtime.getRuntime().availableProcessors()方法获取当前可用线程数,动态的增加每次同时执行任务的数量,进一步加快速度,改用Future或者CompletableFuture ,查询视图优化,子查询太多,为子查询用到的字段添加索引。

本文作者:Weee

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!