使用SXSSFWorkbook导出大量excel表格
2017-11-17 21:18
274 查看
前言:这几天遇到一个需求,需要从数据库读取记录然后导出成excel表格,
在这个过程遇到很多问题,从最开始使用HSSFWorkbook到XSSFWorkbook,最后使用
SXSSFWorkbook。
先来了解下为什么用SXSSFWorkbook,在源码中发现属性名_randomAccessWindowSize,
指定内存中缓存的最大行数,他就是SXSSFWorkbook能导出大量数据关键因素, 具体怎
么实现的呢,一步步查看到SXSSFSheet源码,在创建行的方法中(加粗部分),当达到设
置的最大行数时就flushRows 操作,把当前内存中行数写到磁盘中去
SXSSFWorkbook已经帮我们解决了一大问题了,接下来又遇到问题,我这次导出数据量在
10万行左右,列有15列,一次性查询所有数据的话会造成内存突然大量占用,某些情况可能
会造成服务奔溃,继续分段查询数据
伪代码
maven 引入包
总结,代码虽然能实现大量数据的excel表格导出,但还有优化问题,
1、时间和空间平衡;为了解决上面内存占用问题,我采用了分批次查询数据库,来减小服务
器压力,但这样有引出另个问题,因为多次查询数据库,造成每次数据写完excel后会有等待
数据返回时间,而且对sql查询性能优化显得也更为重要。
2、样式问题:读者可能会奇怪我除了对表头和标题加入样式,正式数据却没有加入样式,
因为在我测试过程中,对每个单元加入样式等其他操作时,会严重影响性能,在一番网上查询
资料未解决后才得以放弃,因为楼主觉得为了样式而牺牲性能有些不值得(毕竟为每个单元格
设置样式可是行列相乘)
希望有读者有更好的方案提出来一起交流,如果发现代码逻辑
有什么错误也请及时指出
在这个过程遇到很多问题,从最开始使用HSSFWorkbook到XSSFWorkbook,最后使用
SXSSFWorkbook。
先来了解下为什么用SXSSFWorkbook,在源码中发现属性名_randomAccessWindowSize,
指定内存中缓存的最大行数,他就是SXSSFWorkbook能导出大量数据关键因素, 具体怎
么实现的呢,一步步查看到SXSSFSheet源码,在创建行的方法中(加粗部分),当达到设
置的最大行数时就flushRows 操作,把当前内存中行数写到磁盘中去
public Row createRow(int rownum) { int maxrow = SpreadsheetVersion.EXCEL2007.getLastRowIndex(); if (rownum >= 0 && rownum <= maxrow) { Row previousRow = rownum > 0 ? this.getRow(rownum - 1) : null; int initialAllocationSize = 0; if (previousRow != null) { initialAllocationSize = previousRow.getLastCellNum(); } if (initialAllocationSize <= 0 && this._writer.getNumberOfFlushedRows() > 0) { initialAllocationSize = this._writer.getNumberOfCellsOfLastFlushedRow(); } if (initialAllocationSize <= 0) { initialAllocationSize = 10; } SXSSFRow newRow = new SXSSFRow(this, initialAllocationSize); this._rows.put(new Integer(rownum), newRow); if (this._randomAccessWindowSize >= 0 && this._rows.size() > this._randomAccessWindowSize) { try { this.flushRows(this._randomAccessWindowSize); } catch (IOException var7) { throw new RuntimeException(var7); } } return newRow; } else { throw new IllegalArgumentException("Invalid row number (" + rownum + ") outside allowable range (0.." + maxrow + ")"); } }
SXSSFWorkbook已经帮我们解决了一大问题了,接下来又遇到问题,我这次导出数据量在
10万行左右,列有15列,一次性查询所有数据的话会造成内存突然大量占用,某些情况可能
会造成服务奔溃,继续分段查询数据
伪代码
public int exportExcel(){ XSSFWorkbook xssfWb = null; SXSSFWorkbook sxssfWorkbook = null; SXSSFSheet sxssSheet = null; FileOutputStream fos = null; try { //内存缓存最大行数 int rowMaxCache = 100; xssfWb = new XSSFWorkbook(); //rowMaxCache(可选)不声明默认100 sxssfWorkbook = new SXSSFWorkbook(xssfWb, rowMaxCache); sxssSheet = (SXSSFSheet) sxssfWorkbook.createSheet("sheet标题名"); //记录总数 int listNum = 查询数据库; //查询数据库最大记录数 int sourceMaxCache = 10000; //总共查询次数 int findNum = 0; if (0 == (listNum % sourceMaxCache)){ findNum = listNum / sourceMaxCache; }else { findNum = listNum / sourceMaxCache + 1; } for (int j = 0; j < findNum; j++){ //mapParam作为查询条件限制,这里只设置设置数据库中的limit mapParam.put("start",j * sourceMaxCache + 1); mapParam.put("end",sourceMaxCache); //导出记录 List<Map<String,Object>> list = 查询数据库操作(mapParam); //行数 int exportNum = list.size(); //列数 int columnNum = list.get(0).size(); // 首行 标题 if (0 == j){ Row row0 = sxssSheet.createRow(0); Cell sxssC1 = row0.createCell(0); sxssC1.setCellValue("标题"); //设置单元格样式 setStyle sxssC1.setCellStyle(setStyle(sxssfWorkbook)); //合并 参数1:起始行号 参数2:终止行号 参数3:起始列号 参数4:终止列号 sxssSheet.addMergedRegion(new CellRangeAddress(0, 0, 0, exportNum)); } //第二行 表头 if (0 == j){ Row row1 = sxssSheet.createRow(1); for (int k = 0; k < columnNum; k++){ // Row row, int column 4000 , String value, CellStyle style // 行 ,列号,单元格值,样式(可选) CellUtil.createCell(row1, 0, value, setStyle(sxssfWorkbook)); } } //数据 for (int i = 0; i < exportNum; i++) { Row row = sxssSheet.createRow(j * sourceMaxCache + i + 2); Map<String, Object> map = list.get(i); // 数据 for (int k = 0; k < columnNum; k++){ // Row row, int column, String value, CellStyle style // 行 ,列号,单元格值 CellUtil.createCell(row1, 0, value); } } //帮助gc回收内存 list.clear(); } //创建文件导出文件路径 File f = new File(currStr); try { if (!f.exists()) { f.createNewFile(); } } catch (IOException e1) { return "0"; } fos = new FileOutputStream(f); sxssfWorkbook.write(fos); } catch (Exception e) { e.printStackTrace(); return "0"; }finally{ try { if(fos != null){ fos.close(); } } catch (IOException e) { e.printStackTrace(); } } }
maven 引入包
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.9</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.9</version> </dependency>
总结,代码虽然能实现大量数据的excel表格导出,但还有优化问题,
1、时间和空间平衡;为了解决上面内存占用问题,我采用了分批次查询数据库,来减小服务
器压力,但这样有引出另个问题,因为多次查询数据库,造成每次数据写完excel后会有等待
数据返回时间,而且对sql查询性能优化显得也更为重要。
2、样式问题:读者可能会奇怪我除了对表头和标题加入样式,正式数据却没有加入样式,
因为在我测试过程中,对每个单元加入样式等其他操作时,会严重影响性能,在一番网上查询
资料未解决后才得以放弃,因为楼主觉得为了样式而牺牲性能有些不值得(毕竟为每个单元格
设置样式可是行列相乘)
希望有读者有更好的方案提出来一起交流,如果发现代码逻辑
有什么错误也请及时指出
相关文章推荐
- Java Excel SXSSFWorkbook大量数据导出
- java使用SXSSFWorkbook生成具有图片与文字的Excel表格
- 使用NPOI将数据库里信息导出Excel表格并提示用户下载
- 如何使用POI导入导出到excel表格
- Java 使用POI实现Excel表格的导入导出
- POI 海量数据/大数据文件生成SXSSFWorkbook使用简介
- 使用 nopi导出大量数据保存execal的方法 c#开发
- java中使用jxl导出Excel表格
- tp框架中使用phpexcel导出excel表格
- 关于Asp.net中使用以下代码导出Excel表格的问题
- 基于springMvc的maven项目,使用jxl导出excel表格
- DataGridView使用技巧—导出Excel表格
- java使用freemarker作为模板导出Excel表格
- java中使用jxl导出excel表格的工具类(全网唯一亲测可用,在原来基础上扩展)
- poi excel大数据导出-SXSSFWorkbook
- 使用POI实现数据导出Excel表格
- C# winform平台下使用spread控件导出excel表格
- 输入int类型数据,返还A....AA....BC等,在使用POI导出Excel表格时导出公式时经常使用。
- 3.POI SXSSF导出大量数据实例
- 使用POI导出Excel表格