EasyExcel封装一个分页写数据的通用方法(保姆级),继上一篇easyExcel导出上线后的优化
Posted 路过人间的姜先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EasyExcel封装一个分页写数据的通用方法(保姆级),继上一篇easyExcel导出上线后的优化相关的知识,希望对你有一定的参考价值。
【EasyExcel】封装一个分页写数据的通用方法
需求:通过elasticsearch查询出来一次性写,在大数据量时存在OOM的隐患分页查询、分批次写数据,避免导出大数据量时内存消耗陡增基于elasticsearch分页查询;mybatis-puls同理
文章目录
前言
在上个博客中解决了线上导出字体依赖的问题,由于涉及的导出模块较多,因为打算封装一个方法做通用导出。
一、所需依赖
1、easyexcel mavn 依赖文件
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
<optional>true</optional>
</dependency>
二、源码实现
1、依赖的枚举类:ExcelPageEnum(自定义)
ExcelPageEnum:
/**
* @className: ExcelPageEnum
* @description: 存储 XLS与XLSX每次最大写入与sheet最大Row的枚举
* @author: 云野
* @date: 2022/8/12
*/
public enum ExcelPageEnum
/**
*
* default export xlsx, page size 10000, sheet max row 1000000
* @author: 云野
* @date: 2022/8/12
*/
XLS(10_000, 60_000),
XLSX(10_000, 1_000_000);
private int pageSize;
private int sheetMaxRow;
ExcelPageEnum(int pageSize, int sheetMaxRow)
this.pageSize = pageSize;
this.sheetMaxRow = sheetMaxRow;
public int getPageSize()
return pageSize;
public int getSheetMaxRow()
return sheetMaxRow;
2、依赖的核心接口类:PageQueryService(自定义)
/**
* @className: PageQueryService
* @description: 实现通用的核心接口
* @author: 云野
* @date: 2022/8/12
*/
@FunctionalInterface
public interface PageQueryService<E>
/**
* 带条件分页查询所有数据
*
* @return Result<List>
*/
List<E> findByPage(Integer current, Integer size);
3、最终调用工具类: EasyExcelUtils
/**
* 通用导出方法
*
* @className: EasyExcelUtils
* @author: 云野
* @date: 2022/8/12
*/
@Slf4j
@Component
public class EasyExcelUtils extends EasyExcel
public static void pageWrite(
ExcelWriter excelWriter,
String sheetName,
Integer totalCount,
HttpServletResponse response,
PageQueryService pageQueryService)
// default export xlsx, page size 10000, sheet max row 1000000
int pageSize = ExcelPageEnum.XLSX.getPageSize();
int sheetMaxRow = ExcelPageEnum.XLSX.getSheetMaxRow();
ExcelTypeEnum excelType = excelWriter.writeContext().writeWorkbookHolder().getExcelType();
boolean isXls = excelType != null && ExcelTypeEnum.XLS.getValue().equals(excelType.getValue());
if (isXls)
pageSize = ExcelPageEnum.XLS.getPageSize();
sheetMaxRow = ExcelPageEnum.XLS.getSheetMaxRow();
try
// 下方使用了用户自己选择文件保存路径的方式,所以需要配请求参数,如果使用固定路径可忽略此代码
String filename = URLEncoder.encode(sheetName + ".xlsx", "UTF-8");
response.setCharacterEncoding("UTF-8");
// 设定输出文件头
response.setHeader("Content-disposition", "attachment; filename=" + filename);
// 定义输出类型
response.setContentType("application/x-xls");
// 这里其实就是把上面的方法分开写,写入同一个sheet
WriteSheet writeSheet = EasyExcelFactory.writerSheet(sheetName).build();
// compute page count, sheet count
long pageCount = (totalCount - 1) / pageSize + 1;
long sheetCount = (totalCount - 1) / sheetMaxRow + 1;
int currentPage = 0;
// page write data
WriteSheet sheet = null;
for (int i = 0; i < sheetCount; i++)
Pageable pageable = new PageRequest(i, pageSize);
sheet = EasyExcelFactory.writerSheet(i, sheetName + i).build();
for (int j = 0; j < (sheetMaxRow / pageSize); j++)
excelWriter.write(pageQueryService.findByPage(currentPage, pageSize), sheet);
currentPage++;
if (currentPage >= pageCount)
break;
catch (Exception e)
e.printStackTrace();
finally
if (excelWriter != null)
excelWriter.finish();
三、代码调用方式
1、Controller层代码
@Slf4j
@Api(tags = "后台--货源服务API")
@RestController
@RequestMapping("/api/admin")
public class AdminCargoOwnerSupplyResource
@Autowired private HttpServletResponse response;
@Autowired private CargoOwnerSupplyService cargoOwnerSupplyService;
/**
* @author: 云野 @Description: 导出货源Excel
* @date: 2022/8/12
* @param: [queryRequest]查询条件对象
*/
@ApiOperation(value = "导出货源", httpMethod = "GET")
@GetMapping(value = "/url")
public void exportExcel(AdminCargoOwnerSupplyQueryRequest queryRequest)
ServletOutputStream outputStream = null;
try
outputStream = response.getOutputStream();
catch (IOException e)
// build excel writer , CargoOwnerSupplyExportExcel 为自定义的导出类
// excelType:导出类型,我这边导出的是XLSX文件
ExcelWriter excelWriter =
EasyExcelFactory.write(outputStream, CargoOwnerSupplyExportExcel.class)
.excelType(ExcelTypeEnum.XLSX)
.build();
// page write ; “CargoOwnerSupply”为写出的文件名字
// cargoOwnerSupplyService.countOfQueryRequest(queryRequest) 是调用的方法,查询需要导出多少数据
EasyExcelUtils.pageWrite(
excelWriter,
"CargoOwnerSupply",
cargoOwnerSupplyService.countOfQueryRequest(queryRequest),
response,
(currentPage, pageSize) ->
cargoOwnerSupplyService.exportExcel(
queryRequest, new PageRequest(currentPage, pageSize)));
2、ExportExcel导出层实体(自定义,也可以是直接的entity实体)
/**
* @Author 云野 @Description: 导出Excel后台货源 @Date 2022/8/12
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CargoOwnerSupplyExportExcel
/** 账号 */
@ExcelProperty(value = "账号", index = 0)
private String account;
/** 联系人 */
@ExcelProperty(value = "联系人", index = 1)
private String contactPerson;
/** 发布时间 */
@ExcelProperty(value = "发布时间", index = 2)
private String createTime;
/** 货物名称 */
@ExcelProperty(value = "货物名称", index = 4)
private String goodsName;
/** 发货量 */
@ExcelProperty(value = "发货量", index = 5)
private Integer deliveryMount;
3、Service接口层
/** @Author 云野 @Description: @Date 2022/8/12 */
public interface CargoOwnerSupplyService
/** 根据条件查询货源数量 */
Integer countOfQueryRequest(AdminCargoOwnerSupplyQueryRequest request);
/**
* 导出Excel
*
* @param request
* @param pageable
*/
List<CargoOwnerSupplyExportExcel> exportExcel(
AdminCargoOwnerSupplyQueryRequest request, Pageable pageable);
4、Service接口实现层
/** @Author 云野 @Description: @Date 2022/8/12 */
public interface CargoOwnerSupplyServiceImpl
/** 根据条件查询货源数量 */
@Override
public Integer countOfQueryRequest(AdminCargoOwnerSupplyQueryRequest request)
// 这是是我Elasticsearch的查询数量的方法,如果是sql的话,用自己的api
return Integer.valueOf(
String.valueOf(
elasticsearchTemplate.count(
new NativeSearchQueryBuilder().withQuery(queryConditions(request)).build(),
CargoOwnerSupplyDTO.class)));
/**
*
* 因为我没有直接使用数据库查询出来的entity实体对象,而是再封装定义了一层ExportExcel实体对象,因此我这边需要准备这个赋值的方式,如果看官们直接用的数据库查询出来的 entity实体,那么就不需要赋值的过程
* @author: 云野
* @Description: 导出Excel
* @date: 2022/8/12
* @param: [request(查询条件), pageable(分页查询)]
* @return: void
*/
@Override
public List<CargoOwnerSupplyExportExcel> exportExcel(
AdminCargoOwnerSupplyQueryRequest request, Pageable pageable)
// 通过条件从数据库查询所有,返回一个List
List<CargoOwnerSupplyVo> cargoOwnerSupplyList =
cargoOwnerSupplyWebMapper.dtos2vos(adminFindAll(request, pageable).getContent());
// 初始化List
ArrayList<CargoOwnerSupplyExportExcel> list = new ArrayList<>();
// 遍历查询出来的 List<CargoOwnerSupplyVo> , 然后进行赋值到 ArrayList<CargoOwnerSupplyExportExcel>的操作
cargoOwnerSupplyList.stream()
.forEach(
vo ->
// 基于@Builder注解进行赋值
CargoOwnerSupplyExportExcel excel = CargoOwnerSupplyExportExcel
.builder()
.account(vo.getContactPhone())
.contactPerson(vo.getContactPerson())
.createTime(vo.getCreateTime())
.goodsName(vo.getGoodsName())
.deliveryMount(vo.getDeliveryMount()).build();
list.add(excel);
);
return list;
三、总结
看过我以前博客的小伙伴应该知道,我这边所有的案例都是开发时碰到的,同时我这边的Data
层用的是Elasticsearch+Spring Data Jpa
,因为公司用的就是这个呀,所以案例中出现 的查询方案案例,如果使用Sql
的小伙伴不需要进行深究,只需要明白一点就行,这种时候可以直接使用Sql
中同样的查询即可。
看到这里如果帮助到了你,给个点赞,谢谢啦!
以上是关于EasyExcel封装一个分页写数据的通用方法(保姆级),继上一篇easyExcel导出上线后的优化的主要内容,如果未能解决你的问题,请参考以下文章
MyBatis-Plus对于大数据量查询,采用分页查询按批次处理结果,通用工具封装
MyBatis-Plus对于大数据量查询,采用分页查询按批次处理结果,通用工具封装
MybatisPlus 快速构建MybatisPlus 原生mybatis(分页查询) 通用枚举 service 封装 自动填充