一、前言
easypoi是为java提供的一款excel导入导出的工具包。使用easypoi,能极大的简化我们excel导入导出的操作;但是在使用过程中,也发现了一些bug,在这里做一些相关记录。
二、问题
我这里发现的问题主要是easypoi提供的模板导出功能。
1.前期准备
为了模拟问题的出现,我们需要新建一个Springboot项目,然后引入相关依赖,如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>demo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.1.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
我是用的easypoi版本为4.1.3。
接着,我们再创建两个实体类,如下:
package com.example.demo.entity;
import lombok.Data;
@Data
public class Student {
private Integer id;
private String stuName;
}
package com.example.demo.entity;
import lombok.Data;
import java.util.List;
@Data
public class Teacher {
private Integer id;
private String name;
List<Student> stuList;
}
两个实体类,一个Teacher类一个Student类,Teacher与Student的关系是一对多的关系。
最后,我们在resources目录下的templates文件夹下,新增一个空白的名字为导出模板.xlsx
的文件作为我们的导出模板,如下:
1.循环嵌套某些单元格值为空的情况
首先,修改我们的导出模板.xlsx
如下:
导出模板语法在这里我就不多说了,详情请参考easypoi模板语法。
接着,编写我们的测试方法。
@Test
void contextLoads() throws Exception{
/**
* 生成需要导出的数据
*/
List<Teacher> teacherList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Teacher t = new Teacher();
t.setId(i);
t.setName("教师_" + i);
List<Student> studentList = new ArrayList<>();
for (int j = 0; j < 3; j++) {
Student student = new Student();
student.setId(j);
student.setStuName("学生_" + i + "_" + j);
studentList.add(student);
}
t.setStuList(studentList);
teacherList.add(t);
}
/**
* 加载excel模板
*/
TemplateExportParams params = new TemplateExportParams(
"templates/导出模板.xlsx", true);
Map<String, Object> map = new HashMap<String, Object>();
/**
* 这个map的key需要和模板里便利的值保持一直
*/
map.put("list", teacherList);
Workbook book = ExcelExportUtil.exportExcel(params, map);
File savefile = new File("D:/home/excel/");
if (!savefile.exists()) {
savefile.mkdirs();
}
FileOutputStream fos = new FileOutputStream("D:/home/excel/exportTemp.xlsx");
book.write(fos);
fos.close();
}
注意:模板里遍历的列表名及对应属性需要和代码中的属性保持一致!
执行我们的测试方法,最终生成的excel如下:
从上图中不难发现,教师0应该也有三个学生,但是最终只显示出一个,其他的两个直接为空白!
2.问题解决
数据丢了两行,我们怎么解决呢?解决方法很简单,只要将模板里面的$fe
替换为fe
就可以了,修改之后的模板如下:
这里补充说明一下,$fe
与fe
的区别。
fe
:在处理数据时,会在插入数据的下方生成一个空行
$fe
:会在插入数据的下方保留原有数据
这里其实是easypoi的一个bug,在处理一对多数据的时候,使用$fe
会有空行的问题。
再次运行我们的测试程序,得到的结果如下:
我们不难发现,原先为空的数据现在全部都展示了。
3.新的问题
解决了上述问题,又有新的问题产生了,如果我们的要求就是需要在列表下方保留一列,比如加一个合计列,这时我们该怎么处理呢?
在这里我说一下我的解决方案。
通过Workbook book = ExcelExportUtil.exportExcel(params, map);
方法返回的Workbook对象,获取到sheet对象,在通过sheet对象获取excel中数据的行数,在获取到的行数中插入我们的代码。修改测试类中代码如下:
@Test
void contextLoads() throws Exception{
/**
* 生成需要导出的数据
*/
List<Teacher> teacherList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Teacher t = new Teacher();
t.setId(i);
t.setName("教师_" + i);
List<Student> studentList = new ArrayList<>();
for (int j = 0; j < 3; j++) {
Student student = new Student();
student.setId(j);
student.setStuName("学生_" + i + "_" + j);
studentList.add(student);
}
t.setStuList(studentList);
teacherList.add(t);
}
/**
* 加载excel模板
*/
TemplateExportParams params = new TemplateExportParams(
"templates/导出模板.xlsx", true);
Map<String, Object> map = new HashMap<String, Object>();
/**
* 这个map的key需要和模板里便利的值保持一直
*/
map.put("list", teacherList);
Workbook book = ExcelExportUtil.exportExcel(params, map);
/**
* 1.首先获取sheet
* 2.通过获取到的sheet获取excel的函数
* 3.在指定行创建单元格并设置值
*/
Sheet sheetAt = book.getSheetAt(0);
/**********************添加合计行的代码开始************************************/
// 设置单元格样式
CellStyle cellStyle = book.createCellStyle();
cellStyle.setAlignment(HorizontalAlignment.CENTER);//设置水平居中
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);//设置垂直居中
int physicalNumberOfRows = sheetAt.getPhysicalNumberOfRows();
Row row = sheetAt.createRow(physicalNumberOfRows);
Cell cell01 = row.createCell(0);
cell01.setCellStyle(cellStyle);
cell01.setCellValue("合计");
/***********************添加合计行的代码结束*******************************/
File savefile = new File("D:/home/excel/");
if (!savefile.exists()) {
savefile.mkdirs();
}
FileOutputStream fos = new FileOutputStream("D:/home/excel/exportTemp.xlsx");
book.write(fos);
fos.close();
}
运行我们的测试程序,结果如下:
可以发现我们的合计列加上了。
这是我自己想到的解决方案,如果大家有更好的解决方案,欢迎大家在评论区评论。
三、总结
在使用easypoi的模板导出功能时,如果导出的数据包含一对多的情形,此时使用$fe
是由bug的,可能会产生空行的问题,此时建议使用fe
替换$fe
。如果列表下面还需要保留数据,需要我们通过Workbook对象,手动插入相关数据。