EasyPOI报表的使用及对象嵌套的Excel报表导入导出
EasyPOI报表介绍简介easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板语言(熟悉的表达式语法),完成以前复杂的写法 。官网文档地址:http://doc.wupaas.com/docs/easypoi/easypoi-1c0u4mo8p4ro8使用<
EasyPOI报表
介绍
简介
easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员
就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板
语言(熟悉的表达式语法),完成以前复杂的写法 。官网文档地址:
http://doc.wupaas.com/docs/easypoi/easypoi-1c0u4mo8p4ro8
使用
<!--easyPoi依赖 --> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-spring-boot-starter</artifactId> <version>4.4.0</version> </dependency>
我是在Springboot项目中使用的,使用场景几乎都差不多,在此处使用注解进行配置
基本使用
项目环境搭建:
maven依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </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> <!-- mybatis-plus 依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.2</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!-- swagger2依赖 --> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <!-- swagger-ui 依赖 --> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>swagger-bootstrap-ui</artifactId> <version>1.9.6</version> </dependency> <!--easyPoi依赖 --> <dependency> <groupId>cn.afterturn</groupId> <artifactId>easypoi-spring-boot-starter</artifactId> <version>4.4.0</version> </dependency> 注意: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.6</version> <relativePath/> <!-- lookup parent from repository --> </parent>
注意:
在项目中使用的springboot版本为2.5.6,该开始创建项目使用的是springboot给我推荐的依赖2.6.0,但是在使用中爆出
Failed to start bean 'documentationPluginsBootstrapper';
,这是使用swagger产生的问题,版本并不兼容,希望各位遇到这个bug时能够快速解决。数据库:(随手建的,你们想尝试的话可以用一下)
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for t_person -- ---------------------------- DROP TABLE IF EXISTS `t_person`; CREATE TABLE `t_person` ( `id` int NOT NULL AUTO_INCREMENT, `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `age` int NULL DEFAULT NULL, `politicsId` int NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_person -- ---------------------------- INSERT INTO `t_person` VALUES (1, 'zhangsan', 20, 1); INSERT INTO `t_person` VALUES (3, '王五', 20, 3); INSERT INTO `t_person` VALUES (4, '王五2', 24, 6); INSERT INTO `t_person` VALUES (5, '王五5', 26, 8); INSERT INTO `t_person` VALUES (6, '李四', 29, 10); INSERT INTO `t_person` VALUES (7, '琪琪', 30, 5); INSERT INTO `t_person` VALUES (8, '季泉', 24, 10); INSERT INTO `t_person` VALUES (9, '汐海', 18, 13); INSERT INTO `t_person` VALUES (10, 'zhangsan', 20, 1); INSERT INTO `t_person` VALUES (11, '王五', 20, 3); INSERT INTO `t_person` VALUES (12, '王五2', 24, 6); INSERT INTO `t_person` VALUES (13, '王五5', 26, 8); INSERT INTO `t_person` VALUES (14, '李四', 29, 10); INSERT INTO `t_person` VALUES (15, '琪琪', 30, 5); INSERT INTO `t_person` VALUES (16, '季泉', 24, 10); INSERT INTO `t_person` VALUES (17, '汐海', 18, 13); -- ---------------------------- -- Table structure for t_politics_status -- ---------------------------- DROP TABLE IF EXISTS `t_politics_status`; CREATE TABLE `t_politics_status` ( `id` int UNSIGNED NOT NULL AUTO_INCREMENT, `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '政治面貌', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 14 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; -- ---------------------------- -- Records of t_politics_status -- ---------------------------- INSERT INTO `t_politics_status` VALUES (1, '中共党员'); INSERT INTO `t_politics_status` VALUES (2, '中共预备党员'); INSERT INTO `t_politics_status` VALUES (3, '共青团员'); INSERT INTO `t_politics_status` VALUES (4, '民革团员'); INSERT INTO `t_politics_status` VALUES (5, '民盟盟员'); INSERT INTO `t_politics_status` VALUES (6, '民建会员'); INSERT INTO `t_politics_status` VALUES (7, '民进会员'); INSERT INTO `t_politics_status` VALUES (8, '农工党党员'); INSERT INTO `t_politics_status` VALUES (9, '致公党党员'); INSERT INTO `t_politics_status` VALUES (10, '九三学社社员'); INSERT INTO `t_politics_status` VALUES (11, '台盟盟员'); INSERT INTO `t_politics_status` VALUES (12, '无党派民主人士'); INSERT INTO `t_politics_status` VALUES (13, '普通公民'); SET FOREIGN_KEY_CHECKS = 1;
代码已经上传到git:
https://gitee.com/XuLiZhao/demo-easypoi
代码及解析
建议前往gitee上获取源码,我这边只对需要注意的点进行讲解。如有疑问请及时联系。
实体类
@Data @EqualsAndHashCode(callSuper = false) @TableName("t_person") @ApiModel(value="Person对象", description="") @ToString public class Person implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Integer id; @ApiModelProperty(value = "姓名") @Excel(name = "姓名",width = 10) private String name; @ApiModelProperty(value = "年龄") @Excel(name = "年龄") private Integer age; @ApiModelProperty(value = "政治面貌") @TableField("politicsId") private Integer politicsId; @ApiModelProperty(value = "政治面貌") @ExcelEntity(name = "政治面貌") @TableField(exist = false) private PoliticsStatus politicsStatus; }
@Data @EqualsAndHashCode(callSuper = false,of = "name") @RequiredArgsConstructor @NoArgsConstructor @TableName("t_politics_status") @ApiModel(value="PoliticsStatus对象", description="") @ToString public class PoliticsStatus implements Serializable { private static final long serialVersionUID = 1L; @TableId(value = "id", type = IdType.AUTO) private Integer id; @ApiModelProperty(value = "政治面貌") @Excel(name="政治面貌") @NonNull private String name; }
这边两个实体类,Person类中包含PoliticsStatus类对象的属性,所以在数据导入或导出时,难度比单表要稍微大一点,需要额外注意。这里还有一个点要注意,就是EqualsAndHashCode这个注解的含义,稍后我会说明。@ExcelEntity是值表格参数为实体类,需要进入该实体类找到@Excel()注解说明的字段才会在表格上展示
Service层
这边没有进行什么有意义的业务处理,只是查询了数据库,获取到所需要的全部信息,为了方便起见,还是贴出来给大家看看。至于为什么还要传入参数id,那就是为了方便其他代码查询单条数据时的复用,毕竟你直接传一个null值就相当查询所有了。
@Service public class PersonServiceImpl extends ServiceImpl<PersonMapper, Person> implements IPersonService { @Autowired private PersonMapper personMapper; @Override public List<Person> getPersons(Integer id) { return personMapper.getPersons(id); } }
Mapper层
接口代码和service几乎差不多,只是多了一个方法体,我主要说明一下mapper.xml文件。在这边用了一个判断,也就是当id为null值时查询所有。返回结果使用resultMap格式,主要是在实体类中我添加了PoliticsStatus对象,但是数据库中并没有字段能够与之对应,然后再使用association为对象赋值。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.xihai.mapper.PersonMapper"> <!-- 通用查询映射结果 --> <resultMap id="BaseResultMap" type="com.xihai.pojo.Person"> <id column="id" property="id" /> <result column="name" property="name" /> <result column="age" property="age" /> <result column="politicsId" property="politicsId" /> </resultMap> <!-- 通用查询结果列 --> <sql id="Base_Column_List"> id, name, age, politicsId </sql> <!--PersonMap--> <resultMap id="PersonMap" type="com.xihai.pojo.Person" extends="BaseResultMap"> <association property="politicsStatus" javaType="com.xihai.pojo.PoliticsStatus"> <id column="pid" property="id"/> <result column="pname" property="name"/> </association> </resultMap> <select id="getPersons" resultMap="PersonMap"> SELECT p.*, ps.id AS pid, ps.NAME AS pname FROM t_person p, t_politics_status ps WHERE p.politicsId = ps.id <if test="null != id"> and p.id = #{id} </if> ORDER BY id </select> </mapper>
关键代码===导入&导出
@RestController
@RequestMapping("/person")
public class PersonController {
@Autowired
private IPersonService personService;
@Autowired
private IPoliticsStatusService politicsStatusService;
@ApiOperation(value = "导出数据")
@GetMapping(value = "/export",produces = "application/octet-stream")
public void getPersons(HttpServletResponse resshujuponse){
List<Person> persons = personService.getPersons(null);
for (Person person : persons) {
System.out.println(person.toString());
}
//参数 第一个为表格的title,第二个为表格的sheetName
ExportParams exportParams = new ExportParams("人员表", "人员表", ExcelType.HSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, Person.class, persons);
ServletOutputStream outputStream = null;
try {
//设置流格式
response.setHeader("content-type", "application/octet-stream");
//设置编码
response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("人员表.xls", "UTF-8"));
outputStream = response.getOutputStream();
workbook.write(outputStream);
}catch (IOException e) {
e.printStackTrace();
} finally {
if (null != outputStream) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@ApiOperation(value = "导入数据")
@PostMapping(value = "/import")
public Result exportPerson(MultipartFile file) {
ImportParams importParams = new ImportParams();
//去标表格的title行以免改行数据被导入
importParams.setTitleRows(1);
List<PoliticsStatus> politicsStatusList = politicsStatusService.list();
try {
//获取ExcelImport导入数据
List<Person> list = ExcelImportUtil.importExcel(file.getInputStream(), Person.class, importParams);
//遍历,通过对象中的名称找到该对象的id值
list.forEach(person -> {
person.setPoliticsId(politicsStatusList.get(politicsStatusList.indexOf(new PoliticsStatus(person.getPoliticsStatus().getName()))).getId());
});
if (personService.saveBatch(list)) {
return Result.success("导入成功!");
}
} catch (Exception e) {
e.printStackTrace();
}
return Result.error("导入失败,请稍后再试!");
}
}
数据导出
导出时注意,你需要在GetMapping中添加
produces = "application/octet-stream"
属性,指定输出的格式,不然你只会看到一大堆的乱码,嘿嘿。之后便是创建导出参数对象,然后通过导出Excel工具类将需要在Excel为文件上输出的对象进行配置就行。@Excel(name=“政治面貌”)这个属性就是表示在Excel上的值,具体怎么称呼呢?如果你知道那亲告诉我一下,哈哈。输出时设置字符编码和响应流格式。使用完流后注意关闭哦。数据导入
重点讲一下这串你们一时半会看不懂的代码吧:
list.forEach(person -> { person.setPoliticsId(politicsStatusList.get(politicsStatusList.indexOf(new PoliticsStatus(person.getPoliticsStatus().getName()))).getId()); });
语句的产生主要是你在导入数据到数据库时插入的是对象id值,而Excel表中的是name值,如果你通过name值查询数据库,当有很多这种对象时你每次都要查,严重影响数据库性能。我这边就是使用了一种方法,通过name值获取对象id值。
以下内容可能混引起不适,亲耐心观看并思考:
- 通过查询所有方法可以得到数据库的所有的记录条数,将每条数据封装为一个java对象并用数组存储起来。
- 使用List接口的 indexOf() 方法可以获取对象所在集合中的位置,然后通过这个位置获取该对象的id值,由于list集合是从数据库里面取出来的数据,所以其中每个对象一定会有id值
- 但是问题又来了,就算你new出一个对象,如何又能去跟集合中的对象去比较获取相同值进而获取对象位置呢。要是对象相等必须要将对象中所参与比较的属性值都相等。那么我可不可以让需要的数据去比较呢?就比如我将Excel报表中的那个属性为name的值去比较?
- 这就要用到lombok插件为我们提供的@EqualsAndHashCode注解,在使用时,将指定的值参与比较,顾名思义,这个注解是针对Equals和HashCode方法的,它会重写这两个方法,也有可能是直接排除,反正不能用了。
- 当PoliticsStatus对象中只用name属性进行比较时,不就可以获取到对象的id了吗?@EqualsAndHashCode注解我会再写一篇博客来说明,到时配上源码让各位更深入理解。
再配几张项目截图吧,让诸位看看效果
这是导出截图:
导入截图
结束,谢谢大家!大家有兴趣可以看看我另一篇关于Apache POI的,不过那篇写的很粗糙,但是知识很底层。
更多推荐
所有评论(0)