前言:这几天遇到一个需求,需要从数据库读取记录然后导出成excel表格,
在这个过程遇到很多问题,从最开始使用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
maven 引入包
org.apache.poi
poi
3.9
org.apache.poi
poi-ooxml
3.9
org.apache.poi
poi-ooxml-schemas
3.9
总结,代码虽然能实现大量数据的excel表格导出,但还有优化问题,
1、时间和空间平衡;为了解决上面内存占用问题,我采用了分批次查询数据库,来减小服务
器压力,但这样有引出另个问题,因为多次查询数据库,造成每次数据写完excel后会有等待
数据返回时间,而且对sql查询性能优化显得也更为重要。
2、样式问题:读者可能会奇怪我除了对表头和标题加入样式,正式数据却没有加入样式,
因为在我测试过程中,对每个单元加入样式等其他操作时,会严重影响性能,在一番网上查询
资料未解决后才得以放弃,因为楼主觉得为了样式而牺牲性能有些不值得(毕竟为每个单元格
设置样式可是行列相乘)
希望有读者有更好的方案提出来一起交流,如果发现代码逻辑
有什么错误也请及时指出