一:问题来源
首先为什么需要分页将数据写入sheet
- 如果把所有数据一次性装载到内存,很容易引起OOM。
- 如果数据量太大,查询sql语句肯定很慢
- 所以目前我采用,分页的方式进行查询,然后单次写入的数据就是查询出来的结果
二:代码示例
1.首先构建公共类
/**
* easypoi导出模板
* @param <T> 查询的入参类型
* @param <R> 导出的结果类型
*/
public abstract class BaseEasyPoiExport<T,R> {
@Resource
private FilePluginFactory filePluginFactory;
/**
* 计算导出数据的总数
*/
protected abstract Long dataTotalCount(T conditions);
/**
* 导出到excel,支持大数量的分批写入
*/
protected void exportExcel(String fileName, T conditions) {
//根据条件查询总记录
Long totalCount = dataTotalCount(conditions);
if (totalCount == 0){
throw new ServiceException("没有数据导出");
}
//每一个Sheet存放的记录数
Long sheetDataRows = eachSheetTotalCount();
//每次写入的数据量
Long writeDataRows = eachTimesWriteSheetTotalCount();
if (totalCount < sheetDataRows) {
sheetDataRows = totalCount;
}
if (sheetDataRows < writeDataRows) {
writeDataRows = sheetDataRows;
}
doExport(fileName, conditions, totalCount, sheetDataRows, writeDataRows);
}
/**
* 导出到excel
*/
private void doExport(String fileName, T conditions, Long totalCount, Long sheetDataRows, Long writeDataRows) {
CompletableFuture.runAsync(() -> {
// 计算需要的Sheet数量
Long sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
// 计算一般情况下每一个Sheet需要写入的次数
Long oneSheetWriteCount = totalCount > sheetDataRows ? sheetDataRows / writeDataRows :
totalCount % writeDataRows > 0 ? totalCount / writeDataRows + 1 : totalCount / writeDataRows;
// 计算最后一个sheet需要写入的次数
Long lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount :
(totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) :
(totalCount / sheetDataRows / writeDataRows + 1));
// 每一个sheet储存的数据
List<Map<String, Object>> sheetsList = new ArrayList<>();
//sheet页的次数
for (int i = 0; i < sheetNum; i++) {
String sheetName = "sheet" + i;
List<R> orderExportVoList = new ArrayList<>();
//如果不是最后一次的写入量,那么就是每次的写入量
for (int j = 0; j < (i != sheetNum - 1 || i == 0 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
buildDataList(orderExportVoList, conditions, j + 1 + oneSheetWriteCount * i, writeDataRows);
}
if (CollectionUtil.isNotEmpty(orderExportVoList)) {
Map<String, Object> exportMap = Maps.newHashMap();
ExportParams exportParams = new ExportParams(null, sheetName, ExcelType.HSSF);
// 以下3个参数为API中写死的参数名 分别是sheet配置/导出类(注解定义)/数据集
exportMap.put("title", exportParams);
exportMap.put("entity", OrderExportVo.class);
exportMap.put("data", orderExportVoList);
// 加入多sheet配置列表
sheetsList.add(exportMap);
}
}
Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
String filePath = filePluginFactory.filePlugin().getFileNamePath(workbook,fileName);
System.out.println(filePath);
});
}
/**
* 每一个sheet存放的数据总数
* 期望是偶数,奇数还未完善
*/
protected abstract Long eachSheetTotalCount();
/**
* 每次写入sheet的总数
* 期望是偶数,奇数还未完善
*/
protected abstract Long eachTimesWriteSheetTotalCount();
/**
* 构建每次查询数量
*/
protected abstract void buildDataList(List<R> resultList, T conditions, Long pageNo, Long pageSize);
}
1.首先将查询条件进行count,看是否有数据
2.计算需要的sheet数,和每次写入的数量
3.实现代码逻辑,分页查询,每次查询的数量等于写入的数量
4.最后将查询出来的数据进行构建,组装成想要导出的数据
5.实现上传阿里云
@Component
public class OrderExportServiceImpl extends BaseEasyPoiExport<OrderExportParams,OrderExportVo> {
@Autowired
private OrderService orderService;
/**
* 数据导出
*/
public void exportWithBigData(String fileName, OrderExportParams conditions) {
this.exportExcel(fileName, conditions);
}
@Override
protected Long dataTotalCount(OrderExportParams conditions) {
Long count = orderService.count(new LambdaQueryWrapper<Order>()
.eq(Order::getDeleteFlag, false)
.eq(StringUtils.isNotBlank(conditions.getShopName()), Order::getShopName, conditions.getShopName()));
return count;
}
@Override
protected Long eachSheetTotalCount() {
return 5000L;
}
@Override
protected Long eachTimesWriteSheetTotalCount() {
return 2000L;
}
@Override
protected void buildDataList(List<OrderExportVo> resultList, OrderExportParams conditions, Long pageNo, Long pageSize) {
conditions.setPageNumber(pageNo.intValue());
conditions.setPageSize(pageSize.intValue());
Page<Order> page = orderService.page(PageUtil.initExportPage(conditions), new LambdaQueryWrapper<Order>()
.eq(Order::getDeleteFlag, false)
.eq(StringUtils.isNotBlank(conditions.getShopName()), Order::getShopName, conditions.getShopName())
.select(Order::getShopName,Order::getOrderNum,Order::getLogisticsNo));
List<Order> records = page.getRecords();
if (CollectionUtil.isEmpty(records)){
return;
}
for (Order record : records) {
OrderExportVo orderExportVo = new OrderExportVo();
BeanUtil.copyProperties(record,orderExportVo);
resultList.add(orderExportVo);
}
}
}
三:总结
此次的导出结果其实和上一篇的效果是一样的,可以去看easyPoi导出多sheet页(干货)-优快云博客
当然这里面可能也有一些不足之处,希望各位大佬能纠正