需求:需要从数据库查询出几十上百万的数据并导出成excel文件
问题:
1、传统导出方式在几万条数据时还可以胜任,但是数量一旦有几十万甚至上百万的话就会出现内存不够用,内存溢出等问题。
2、大批量导出一般会和业务结合比较紧密,如何抽象出通用工具类
技术关键点:
1、解决大批量导出内存消耗问题
2、通用类的抽象
实现思路:
1、因为大部分内存问题发生在查询数据库和生成excel两个地方,所以针对这两个地方进行改造,
第一点,查询时使用分页方式进行查询
第二点,每生成1W条数据时生成一个excel文件到硬盘中,最后将多个excel打包成一个zip文件并导出。同时在使用POI插件进行处理时进行刷盘控制(详细看后边的代码示例,或者参考POI官方文档)
2、抽象工具类中需要实现业务上的分页查询逻辑,然后还要实现异步导出逻辑,可以考虑使用抽象类实现,在关键业务对象上交给子类实现(模板模式)
核心代码(注意代码可能不全,主要是核心的代码类和方法,辅助的代码类比如http上传之类的没有加入,自己实现就好了)
public abstract class BaseExportService<I, O, TI, TO, AM> {
private static final String EVENT_NAME = "大批量导出抽象服务";
private static final int ACT_GOODS_EXPORT_LIMIT_DEFAULT = 100;
private static final int IS_SYNC_EXPORT_MAX_NUM_DEFAULT = 100;
private static final int ACT_GOODS_EXPORT_DIVIDE_PAGE_DEFAULT = 100;
private static final int ACT_GOODS_EXPORT_LIMIT_EXCEL_DEFAULT = 100;
/**
* <b>描述:</b> 导出方法<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param params
* @return O
*/
public O export(I params) throws Exception {
// 获取活动id对应活动信息的map
TI query = getQuery(params);
// 查询总条数
int count = getExportExcelCount(params, query);
if (count == 0) {
throw new ExportBatchException(GlobalErrorCode.DATA_NULL_ERROR);
}
// 查询配置的导出上限
int exportDataLimit = getExportDataLimit();
if (count > exportDataLimit) {
throw new ExportBatchException(GlobalErrorCode.DATA_MAX_LIMIT_ERROR);
}
// 导出逻辑
FileFidModel fileFidModel = coreExportHandle(params, count, query);
return assembleResult(params, fileFidModel, query);
}
/**
* <b>描述:</b> 获取查询条件<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param params
* @return TI
*/
protected abstract TI getQuery(I params);
/**
* <b>描述:</b> 获取导出的总条数<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param params
* @param query
* @return int
*/
protected abstract int getExportExcelCount(I params, TI query);
/**
* <b>描述:</b> 获取生成文件名称的前置名称<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param
* @return java.lang.String
*/
protected abstract String getPrefixName(I params);
/**
* <b>描述:</b> 获取sheet名称<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param
* @return java.lang.String
*/
protected abstract String getSheetName(I params);
/**
* <b>描述:</b> 写入其他信息到最终的列表中<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param selectedGoodsList
* @return java.util.List<AM>
*/
protected abstract List<AM> assembleOtherInfos(List<TO> selectedGoodsList);
/**
* <b>描述:</b> 分页查询数据信息<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param query
* @param pager
* @return java.util.List<TO>
*/
protected abstract List<TO> getInfoListByPager(TI query, Pager pager) throws Exception;
/**
* <b>描述:</b> 获取导出excel的标题行<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param params
* @return java.lang.String[][]
*/
protected abstract String[][] getExportTitle(I params);
/**
* <b>描述:</b> 组装为返回的数据<br/>
* <b>作者:</b>jeff.meng<br/>
* <b>版本:</b>V1.0 <br/>
*
* @param params
* @param fileFidModel
* @param query
* @return O
*/
protected abstract O assembleResult(I params, FileFidModel fileFidModel, TI query);
private List<String> asyncExportExcelNew(I params, TI query) throws Exception {
ActExportHelper actExportHelper = new ActExportHelper();
int pageSize = getExportDividePage();
int divideExcelNum = getExportExcelDataLimit();
int t