Spring Boot 实现主表+明细表 Excel 导出(EasyPOI 实战)
本文基于 Spring Boot + MyBatis-Plus + EasyPOI 实现多个业务模块)的主表带明细表导出 Excel 功能,涵盖多条件筛选、主表明细组装、批量导出,附详细示例与优化建议。
文章目录
前言
在日常生产管理系统开发中,数据导出 Excel 报表是极为常见的需求,无论是生产数据、检测记录,还是统计分析结果,用户通常都希望能够一键导出,方便后续归档、分析和汇报。而很多业务场景不仅仅是导出单表数据,更常见的是主表+明细表的关联数据导出,比如:
- 产品批次+检测明细
- 项目记录+工序明细
- 订单信息+订单项详情
如果不采用成熟方案,自己拼接 Apache POI 导出代码,工作量大、代码复杂、后期维护困难。因此,本文基于Spring Boot + MyBatis-Plus + EasyPOI框架,整理了一套主表带明细表 Excel 导出的完整实现方案。
希望这篇文章能帮到正在开发或即将实现类似功能的你,少走一些弯路,高效完成稳定可靠的数据导出模块。
一、EasyPOI简介
EasyPOI 是一个优秀的基于 Apache POI 封装的 Excel 导入导出框架,注解驱动,简单高效,特别适合 Spring Boot 场景。
📖 什么是 EasyPOI?
EasyPOI 是一款基于 Apache POI 封装的 Java Excel 文档导入导出工具,专门面向企业级 Java Web 开发场景,简化了 Excel、Word 等 Office 文件的读写操作。
相比原生 Apache POI,EasyPOI 在 API 设计、注解驱动、复杂表格映射、导出样式设置等方面做了高度封装,大大降低了开发门槛,常见的导入导出需求无需手动拼接行列、设置单元格样式,只需配置注解即可完成。
📌 核心特性:
📑 基于注解映射:通过 @Excel、@ExcelCollection 等注解,自动将实体类与 Excel 列映射,简化开发流程。
📄 支持多表头、多sheet、多级嵌套:便捷实现主表+明细表、一对多数据结构的导出。
🔍 支持模板导出:通过预设 Excel 模板,填充动态数据,保持样式不变。
📥📤 导入、导出功能完善:支持 Excel 03(.xls)、Excel 07(.xlsx)双格式导入导出。
🎨 支持丰富的样式设置:如字体、颜色、行高列宽、对齐方式、冻结行列、隐藏列、下拉框校验等。
📊 基于 Apache POI,稳定可靠,性能优良。
📌 常见 Java Excel 读写方案对比
工具库 | 特点 | 使用复杂度 | 功能完整性 | 优劣分析 |
---|---|---|---|---|
Apache POI | 原生 Java Office 文件操作库,功能全面 | 高 | 高 | 功能全但编码繁琐,手动操作行列单元格,样式复杂 |
EasyExcel (阿里开源) | 性能优异,超大 Excel 文件读写,事件驱动型 | 中 | 中 | 擅长百万级数据导入导出,但复杂表头、嵌套结构、模板样式不如 EasyPOI |
JXLS | 基于 Excel 模板导出,表达式驱动 | 中 | 中 | 适合固定格式模板报表,但灵活性和复杂结构支持有限 |
EasyPOI | 注解驱动,快速开发,复杂表头、明细、模板导出都支持 | 低 | 高 | 易上手,功能全面,适合企业级应用场景 |
📌 为什么选择 EasyPOI?
在我们实际项目开发中,比如OD密度检测记录、颗粒度检测记录、应力ORT记录等,需要导出主表+多条明细表的 Excel 文件,且要求:
导出文件带多级表头
支持不同检测类型记录共用导出方法
明细与主表数据一键映射
导出样式统一美观、支持模板样式
功能健全、简单易上手、便于后续维护
而经过对比:
原生 Apache POI → 功能太底层,开发效率低,维护成本高
EasyExcel → 虽然性能好,但主表+明细表嵌套导出、复杂表头样式不太方便
EasyPOI → 完全满足我们这类典型生产管理系统的数据导出需求,注解式开发,主表+明细表一对多映射超简单,支持模板,支持丰富样式,社区活跃,文档齐全
因此,我们最终选择了 EasyPOI 作为本项目 Excel 导入导出模块的核心组件。
二、使用步骤
1.pom文件导入相关依赖
代码如下(示例):
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
2.实体类加上配置 注解 @Excel(name = XXX)
主表,代码如下(示例):
其中涉及到集合的明细表使用***@ExcelCollection(name =xx)注解***
@Data
public class DfOrtStressResult {
@Excel(name = "项目")
private String project;
@Excel(name = "批次")
private String batch;
@ExcelCollection(name = "丝印-应力明细")
private List<DfOrtStressDetail> dfOrtStressDetailList;
}
明细表,代码如下(示例):
其中有日期格式的使用 ***@Excel(name = “测试时间”, format = “yyyy-MM-dd HH:mm:ss”)***注解
@Data
public class DfOrtStressDetail {
@Excel(name = "测试时间", format = "yyyy-MM-dd HH:mm:ss")
private Date testTime;
@Excel(name = "CS值")
private Double cs;
}
⚠️ 注意:
@ExcelCollection 注解用于主表集合属性,EasyPOI自动关联导出子表内容。
3.Controller导出接口实现
📌 多条件筛选 + 主表查询
QueryWrapper<DfOrtStressResult> queryWrapper = new QueryWrapper<>();
if (StringUtils.isNotBlank(batch)) {
queryWrapper.like("batch", batch);
}
List<DfOrtStressResult> resultList = dfOrtStressResultService.list(queryWrapper);
📌 批量查询明细 + 分组
List<String> batchList = resultList.stream().map(DfOrtStressResult::getBatch).distinct().collect(Collectors.toList());
List<DfOrtStressDetail> detailList = dfOrtStressDetailService.list(
new QueryWrapper<DfOrtStressDetail>().in("batch", batchList)
);
Map<String, List<DfOrtStressDetail>> detailMap = detailList.stream()
.collect(Collectors.groupingBy(DfOrtStressDetail::getBatch));
resultList.forEach(result ->
result.setDfOrtStressDetailList(detailMap.getOrDefault(result.getBatch(), new ArrayList<>()))
);
📌 Excel导出
ExportParams exportParams = new ExportParams("应力ORT记录", "ORT导出记录");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, DfOrtStressResult.class, resultList);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
String fileName = URLEncoder.encode("应力ORT导出.xlsx", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
workbook.write(response.getOutputStream());
workbook.close();
4.主表+明细组装技巧
核心原则:
✔ 批量查主表
✔ 提取批次集合
✔ 批量查明细,groupingBy 分组
✔ forEach 挂载明细
优点:
✅ 避免 N+1 查询
✅ 查询效率高
✅ 结构清晰,易维护
5. Excel导出效果
- 主表字段 → 顶层表头
- 明细字段 → @ExcelCollection 子表表头
- 多条明细自然关联到对应主表行
6. 总结与优化建议
✔ EasyPOI注解简单直观,适合中小型数据导出场景
✔ 批次+分组查询避免性能问题
✔ Excel导出前建议限制数据量,或分页导出
进阶可用:
- EasyExcel 替代大数据量导出
- 动态列头/动态 sheet 导出
三、完整代码
@GetMapping("/exportDfOrtStressResult")
@ApiOperation(value = "导出记录及明细Excel")
public void exportDfOrtStressResult(
@RequestParam(required = false) String batch,
@RequestParam(required = false) String project,
@RequestParam(required = false) String color,
@RequestParam(required = false) String process,
HttpServletResponse response) throws IOException {
// 查询主表条件
QueryWrapper<DfOrtStressResult> queryWrapper = new QueryWrapper<>();
if (StringUtils.isNotBlank(batch)) {
queryWrapper.like("batch", batch);
}
if (StringUtils.isNotBlank(project)) {
queryWrapper.like("project", project);
}
if (StringUtils.isNotBlank(color)) {
queryWrapper.like("color", color);
}
if (StringUtils.isNotBlank(process)) {
queryWrapper.like("process", process);
}
queryWrapper.orderByDesc("create_time");
// 查询主表数据
List<DfOrtStressResult> resultList = dfOrtStressResultService.list(queryWrapper);
if (!resultList.isEmpty()) {
// 批量提取批次
List<String> batchList = resultList.stream()
.map(DfOrtStressResult::getBatch)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
if (!batchList.isEmpty()) {
// 查询所有明细
for (DfOrtStressResult dfOrtStressResult : resultList) {
QueryWrapper<DfOrtStressDetail> detailWrapper = new QueryWrapper<>();
detailWrapper.eq("batch", dfOrtStressResult.getBatch());
detailWrapper.eq("project", dfOrtStressResult.getProject());
detailWrapper.eq("color", dfOrtStressResult.getColor());
detailWrapper.eq("process", dfOrtStressResult.getProcess());
List<DfOrtStressDetail> list = dfOrtStressDetailService.list(detailWrapper);
dfOrtStressResult.setDfOrtStressDetailList(list);
}
}
}
// 导出Excel
ExportParams exportParams = new ExportParams("ORT记录", "记录");
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, DfOrtStressResult.class, resultList);
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("UTF-8");
String fileName = URLEncoder.encode("记录导出.xlsx", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
// 写出Excel
workbook.write(response.getOutputStream());
workbook.close();
}
📌 接口调用示例:
GET http://localhost:8080/你的路径/exportDfOrtStressResult?batch=202406&project=XX&color=XX&process=XX
***⚠️ 注意:***在postman测试工具上可能测试不了,需要在页面直接输入地址进行测试
总结
导出是生产项目中常用功能,合理利用 EasyPOI+MyBatis-Plus 查询组装技巧,不仅提升开发效率,也保障系统稳定性。👍
本文介绍了基于Spring Boot+MyBatis-Plus+EasyPOI实现主表带明细表Excel导出的完整方案。通过EasyPOI的@Excel和@ExcelCollection注解,可以轻松映射实体类与Excel列的关系。文章详细讲解了从多条件筛选、批量查询明细到Excel导出的实现步骤,提供了主表明细数据组装的高效技巧,并对比了常见Java Excel读写方案。该方案避免了原生POI的复杂性,实现了简洁高效的数据导出功能。