刚刚开通了一个公众号,会分享一些技术博客和自己觉得比较好的项目,同时会更新一些自己使用的工具和图书资料,后面会整理一些面试资料进行分享,觉得有兴趣的可以关注一下。
项目场景:
最近在做一个导出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;
}
这三者的关系,Test1Entity
跟Test2Entity
是一对多的关系,Test2Entity
跟Test3Entity
是一对多的关系,
配置好关联关系后,我们直接使用之前博客的介绍的代码,生成即可。
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
之后发现这个问题就修复了。