easypoi一对多对多导出excel格式问题,数据不对应

刚刚开通了一个公众号,会分享一些技术博客和自己觉得比较好的项目,同时会更新一些自己使用的工具和图书资料,后面会整理一些面试资料进行分享,觉得有兴趣的可以关注一下。
在这里插入图片描述

项目场景:

最近在做一个导出excel报表的需求,数据是三表关联出来的(关系一对多对多)。导出要求对应三者的关系,将对应的子数据,父节点数据要进行合并单元格。
这种场景直接使用easyPOI一键导出很方便,只要配置相对应的注解标注各自的对应关系即可。
实体类代码如下:

  • 实体类1
@Data
@ExcelTarget("test2Entity")
public class Test1Entity {

    @Excel(name = "property1", width = 17.0)
    private String property1;

    @Excel(name = "property2",orderNum = "1")
    private String property2;

    @ExcelCollection(name="Test2Entity信息", orderNum = "6")
    private List<Test2Entity> list;

}
  • 实体类2
@Data
@ExcelTarget("test2Entity")
public class Test2Entity {

    @Excel(name = "property1-1", width = 17.0)
    private String property1_1;

    @Excel(name = "property2-2",orderNum = "1")
    private String property2_2;

    @ExcelCollection(name="Test3Entity信息", orderNum = "6")
    private List<Test3Entity> list;

}
  • 实体类3
@Data
@ExcelTarget("test1Entity")
public class Test3Entity {

    @Excel(name = "姓名", width = 17.0)
    private String name;

    @Excel(name = "年龄",orderNum = "1")
    private Integer age;

    @Excel(name = "电话",orderNum = "2", width = 17.0)
    private String mobile;

}

这三者的关系,Test1EntityTest2Entity是一对多的关系,Test2EntityTest3Entity是一对多的关系,
配置好关联关系后,我们直接使用之前博客的介绍的代码,生成即可。

ExcelExportUtil.exportExcel(new ExportParams("test", "result", ExcelType.XSSF),
                Test1Entity.class, list);

导出即可!


问题描述

果然没有想到的那么简单,生成的文件,数据是对应不上的,老是少数据,开始我以为是对应关系开始就差错了,debug数据看其实是正确的,那么只能看源代码了。


原因分析:

我使用的easyPOI的版本是4.1.0,核心创建excel的代码如下(在BaseExportService类中):

	public int[] createCells(Drawing patriarch, int index, Object t,
                             List<ExcelExportEntity> excelParams, Sheet sheet, Workbook workbook,
                             short rowHeight, int cellNum) {
        try {
            ExcelExportEntity entity;
            Row               row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index);
            if (rowHeight != -1) {
                row.setHeight(rowHeight);
            }
            int maxHeight = 1, listMaxHeight = 1;
            // 合并需要合并的单元格
            int margeCellNum = cellNum;
            int indexKey     = createIndexCell(row, index, excelParams.get(0));
            cellNum += indexKey;
            for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) {
                entity = excelParams.get(k);
                if (entity.getList() != null) {
                    Collection<?> list      = getListCellValue(entity, t);
                    int           listIndex = 0, tmpListHeight = 0;
                    if (list != null && list.size() > 0) {
                        int tempCellNum = 0;
                        for (Object obj : list) {
                            int[] temp = createCells(patriarch, index + listIndex, obj, entity.getList(), sheet, workbook, rowHeight, cellNum);
                            tempCellNum = temp[1];
                            tmpListHeight += temp[0];
                            listIndex++;
                        }
                        cellNum = tempCellNum;
                        listMaxHeight = Math.max(listMaxHeight, tmpListHeight);
                    }
                } else {
                    Object value = getCellValue(entity, t);

                    if (entity.getType() == BaseEntityTypeConstants.STRING_TYPE) {
                        createStringCell(row, cellNum++, value == null ? "" : value.toString(),
                                index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity),
                                entity);

                    } else if (entity.getType() == BaseEntityTypeConstants.DOUBLE_TYPE) {
                        createDoubleCell(row, cellNum++, value == null ? "" : value.toString(),
                                index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity),
                                entity);
                    } else {
                        createImageCell(patriarch, entity, row, cellNum++,
                                value == null ? "" : value.toString(), t);
                    }
                    if (entity.isHyperlink()) {
                        row.getCell(cellNum - 1)
                                .setHyperlink(dataHandler.getHyperlink(
                                        row.getSheet().getWorkbook().getCreationHelper(), t,
                                        entity.getName(), value));
                    }
                }
            }
            maxHeight += listMaxHeight - 1;
            if (indexKey == 1 && excelParams.get(1).isNeedMerge()) {
                excelParams.get(0).setNeedMerge(true);
            }
            for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) {
                entity = excelParams.get(k);
                if (entity.getList() != null) {
                    margeCellNum += entity.getList().size();
                } else if (entity.isNeedMerge() && maxHeight > 1) {
                    for (int i = index + 1; i < index + maxHeight; i++) {
                        if(sheet.getRow(i) == null ) {
                            sheet.createRow(i);
                        }
                        sheet.getRow(i).createCell(margeCellNum);
                        sheet.getRow(i).getCell(margeCellNum).setCellStyle(getStyles(false, entity));
                    }
                    PoiMergeCellUtil.addMergedRegion(sheet, index, index + maxHeight - 1, margeCellNum, margeCellNum);
                    margeCellNum++;
                }
            }
            return new int[]{maxHeight, cellNum};
        } catch (Exception e) {
            LOGGER.error("excel cell export error ,data is :{}", ReflectionToStringBuilder.toString(t));
            LOGGER.error(e.getMessage(), e);
            throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e);
        }

    }

问题的原因找到了,代码在判断是List的时候,会进行循环递归,传过去的值是RowIndex的数据,每次循环,listIndex这个RowIndex会增加,也就是每行创建一条数据。如果这个是单层一对多的关联关系是没有问题的。
但是!!如果出现像我这种一对多对多的这种复杂关系,虽然递归嵌套也能处理,当从最深层返回上一层对应关系的时候,RowIndex会回退!!!导致数据覆盖!!!


解决方案:

我本来是想重写的,只需要回退的时候,记住增加了几个增加上去就好了,但是想看看高版本有没有修复,升级到4.5.0之后发现这个问题就修复了。

### 实现 Excel一对关系的数据导出 为了实现在 Excel 文件中导出一对关系的数据,可以采用特定工具和框架来简化这一过程。对于 Java 开发者而言,在 Spring Boot 应用程序中利用 EasyPOI 或 JXL 等库能够高效完成此操作。 #### 使用 EasyPOI 进行一对数据导出 EasyPOI 是一款用于处理 Excel 的开源组件,支持种复杂场景下的数据读取与写入功能。针对一对的关系模型,可以通过 `@Excel` 和 `@ExcelCollection` 注解配合使用来描述父子表之间的关联[^1]。 具体来说: - **父级实体类**:需标注 `@Excel` 来指定列属性; - **子集集合字段**:应标记有 `@ExcelCollection(name="子项名称")` 并指明对应的子项列表名; 下面是一个简单的例子展示如何设置这些注解以及调用导出函数: ```java // 定义项目组 VO 类 (父级) public class ProjectGroupExcelVO { private String groupName; @ExcelCollection(name = "projects") private List<Project> projects; // getter and setter methods... } // 定义项目 VO 类 (子级) public class Project { @Excel(name = "projectName", width = 20) private String projectName; // other fields... // getter and setter methods... } ``` 当准备好了相应的实体映射之后,就可以通过如下方式执行实际的文件生成逻辑了: ```java // 准备好要导出的对象实例化并填充数据 ProjectGroupExcelVO groupVo = new ProjectGroupExcelVO(); groupVo.setGroupName("Example Group"); List<Project> projectList = Arrays.asList( new Project("Proj A"), new Project("Proj B")); groupVo.setProjects(projectList); // 创建参数配置对象 ExportParams exportParams = new ExportParams(); // 执行导出动作 Workbook workbook = ExcelExportUtil.exportExcel(exportParams, ProjectGroupExcelVO.class, Collections.singletonList(groupVo)); FileOutputStream fos = null; try{ fos = new FileOutputStream(new File("/path/to/output.xlsx")); workbook.write(fos); } finally { IOUtils.closeQuietly(fos); } ``` 这段代码片段展示了怎样构建包含一对结构的信息,并将其转换成 Excel 表格形式保存到本地磁盘上。 #### 处理复杂的表格布局 除了基本的一对导出外,有时还需要考虑更复杂的排版需求,比如合并单元格或者自定义样式等特性。这通常涉及到调整模板设计或是进一步定制 `ExportParams` 对象中的选项[^2]。 例如,如果希望在导出时控制某些特殊格式,则可以在初始化 `ExportParams` 时传入额外的参数来进行个性化设定。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦幻D开始

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值