Spring Boot 实现主表+明细表 Excel 导出(EasyPOI 实战)

本文基于 Spring Boot + MyBatis-Plus + EasyPOI 实现多个业务模块)的主表带明细表导出 Excel 功能,涵盖多条件筛选、主表明细组装、批量导出,附详细示例与优化建议。

文章目录

  • 前言
  • 一、EasyPOI简介
      • 常见 Java Excel 读写方案对比
      • 为什么选择 EasyPOI?
  • 二、使用步骤
    • 1.pom文件导入相关依赖
    • 2.实体类加上配置 注解 @Excel(name = XXX)
    • 3.Controller导出接口实现
      • 多条件筛选 + 主表查询
      • 批量查询明细 + 分组
      • Excel导出
    • 4.主表+明细组装技巧
    • 5. Excel导出效果
    • 6. 总结与优化建议
  • 三、完整代码
      • 接口调用示例:
  • 总结


前言

在日常生产管理系统开发中,数据导出 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.afterturngroupId>
    <artifactId>easypoi-spring-boot-starterartifactId>
    <version>4.4.0version>
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的复杂性,实现了简洁高效的数据导出功能。

你可能感兴趣的:(java,spring,boot,excel,后端,java,EasyPOI,excel导出)