在我们日常工作中经常需要导入和导出我们的员工数据,所以本次我们完善此功能
poi :poi就是批量的操作文件或数据的导入以及导出 ,但是因为其使用起来需要过多的编辑代码,所以我们在此采用easypoi。
国内有很多开源项目对poi进行了封装,大大减少代码量,使其能够更简单的被我们使用并提高开发效率,例如EasyPoi,Excel4J,HuTools等优秀的开源项目。我们这次以EasyPoi为例
Easypoi功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员 就可以方便的写出Excel出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板 语言(熟悉的表达式语法),完成以前复杂的写法。
特点:
设计精巧,使用简单
接口丰富,扩展简单
默认值多,write less do more
AbstractView支持,web导出可以简单明了
<dependency>
<groupId>cn.afterturngroupId>
<artifactId>easypoi-spring-boot-starterartifactId>
<version>4.1.3version>
dependency>
EasyPoi最大的特点是&#xff1a;可以通过注解定义导出的字段。
普通属性&#xff1a;员工类使用注解&#64;Excel 定义了需要导出的属性&#xff0c; name 为导出的列名&#xff0c; width 可以定义列的宽度&#xff0c; format可以定义导入和导出的日期格式
实体类对象作为属性&#xff1a;员工类中还有其他类作为属性&#xff0c;例如民族&#xff0c;政治面貌&#xff0c;职称&#xff0c;职位等。这些使用&#64;ExcelEntity 标记为实体类。
并且在该实体类对象中加入注解 &#64;Excel即可。
修改pojo包下的Employee类
employee类是员工类:
/**
* &#64;Excel 注解的部分参数&#xff1a;
* name:导出到excel的列名
* width:是导入到excel时&#xff0c;表格的宽度
* suffix &#61; "年" 后缀&#xff0c;比如说工龄是1年
*/
---------------------- ----------------------
/**
* 民族、婚姻政治面貌、部门、职位等肯定不是导入导出id&#xff0c;而是导出对应的表中的名字
* 以民族为例子&#xff1a;
* 民族nation是一个对象&#xff0c;也是POJO类&#xff0c;那怎么定义实体类呢&#xff1f;
* 第一步&#xff1a;加入注解 &#64;ExcelEntity
* 第二部&#xff1a;修改POJO类 加入&#xff1a;&#64;Excel(name &#61; "民族")
*
* -- 那么会通过注解ExcelEntity表示是一个实体类&#xff0c;从而导入真实的数据
*
*/
&#64;ApiModelProperty(value &#61; "入职日期")
&#64;JsonFormat(pattern &#61; "yyyy-MM-dd",timezone &#61; "Asia/Shanghai")
&#64;Excel(name &#61; "入职日期",width &#61; 20, format &#61; "yyyy-MM-dd")
private LocalDate beginDate;
&#64;ApiModelProperty(value &#61; "在职状态")
&#64;Excel(name &#61; "在职状态")
private String workState;
&#64;ApiModelProperty(value &#61; "工号")
&#64;Excel(name &#61; "工号")
private String workID;
&#64;ApiModelProperty(value &#61; "合同期限")
&#64;Excel(name &#61; "合同期限",suffix &#61; "年")
private Double contractTerm;
/**
* 表中有很多外界id&#xff0c;都需要对应外界的表中。
* 表需要对应pojo的对象中
*/
&#64;ApiModelProperty(value &#61; "民族")
//在正常的员工表中是不存在的
&#64;TableField(exist&#61;false)
&#64;ExcelEntity(name &#61; "民族")
private Nation nation;
&#64;ApiModelProperty(value &#61; "政治面貌")
&#64;TableField(exist &#61; false)
&#64;ExcelEntity(name &#61; "政治面貌")
private PoliticsStatus politicsStatus;
&#64;ApiModelProperty(value &#61; "部门")
&#64;TableField(exist &#61; false)
&#64;ExcelEntity(name &#61; "部门")
private Department department;
&#64;ApiModelProperty(value &#61; "职称")
&#64;TableField(exist &#61; false)
&#64;ExcelEntity(name &#61; "职称")
private Joblevel joblevel;
&#64;ApiModelProperty(value &#61; "职位")
&#64;TableField(exist&#61; false)
&#64;ExcelEntity(name &#61; "职位")
private Position position;
&#64;ApiModelProperty(value &#61; "工资账套")
&#64;TableField(exist &#61; false)
private Salary salary;
再去修改nation类中的代码&#xff1a;
package com.xxxx.server.pojo;
import cn.afterturn.easypoi.excel.annotation.Excel;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.*;
import lombok.experimental.Accessors;
&#64;Data
//of: 要重写的属性。 以name重写了Equals和hashcode。表示以name重写了equals/hashcode以及写完了
//当新建对象的时候&#xff0c;可以直接把name放进去&#xff0c;表示得到一个唯一的。&#xff1a;有参构造
&#64;RequiredArgsConstructor
&#64;NoArgsConstructor
&#64;EqualsAndHashCode(callSuper &#61; false,of &#61; "name")
&#64;Accessors(chain &#61; true)
&#64;TableName("t_nation")
&#64;ApiModel(value&#61;"Nation对象", description&#61;"")
public class Nation implements Serializable {
private static final long serialVersionUID &#61; 1L;
&#64;ApiModelProperty(value &#61; "id")
&#64;TableId(value &#61; "id", type &#61; IdType.AUTO)
private Integer id;
&#64;ApiModelProperty(value &#61; "民族")
&#64;Excel(name &#61; "民族")
//非空的&#xff0c;写新的有参构造时&#xff0c;name是必填的。
&#64;NonNull
private String name;
}
//通过流的形式传出去&#xff1a;httpServletResponse response
// produces通过流形式传输&#xff0c;解决乱码
&#64;ApiOperation(value &#61; "导出员工数据")
&#64;GetMapping(value &#61; "/export",produces &#61; "application/octet-stream")
public void exportEmployee(HttpServletResponse response) throws UnsupportedEncodingException {
//获取当前员工的数据-具体的数据
List<Employee> list &#61; iEmployeeService.getEmployee(null);
/**
* 导出的参数&#xff1a;
* 导出员工的数据&#xff1a;&#xff08;文件名的名字&#xff0c;sheet表名&#xff0c;excel的版本&#xff09;
* excel &#xff1a;
* 03&#xff1a; HSSF &#xff0d; 提供读写Microsoft Excel XLS格式档案的功能。03打不开07
* 07&#xff1a; XSSF &#xff0d; 提供读写Microsoft Excel OOXML XLSX格式档案的功能。07能打开03
*
* 03的HSSF比07的XSSF优点&#xff1a;
* 1.速度快
* 2.兼容性好。03打不开07&#xff0c; 07能打开03
*/
ExportParams params &#61; new ExportParams("员工表","sheet员工表", ExcelType.HSSF);
/**
* ExcelExportUtil导出工具类&#xff1a;&#xff08;导出的参数、对象类名.class、具体的数据&#xff09;
* 返回的是Workbook。是poi的一个类。相当于工作簿&#xff08;工作簿就是一个Excel&#xff09;
*/
Workbook book &#61; ExcelExportUtil.exportExcel(params, Employee.class, list);
/**
* response。输出流形式 输出workboo
*
* 导出需要一些请求头的响应信息
* 防止中文乱码
*/
ServletOutputStream out &#61; null;
try{
//流形式&#xff0c;设置一个头
response.setHeader("content-type","application/octet-stream");
//防止中文乱码
response.setHeader("content-disposition","attachment;filename&#61;"&#43;
URLEncoder.encode("员工表.xls","UTF-8"));
out &#61; response.getOutputStream();
book.write(out);
}catch (Exception e){
e.printStackTrace();
}finally {
try{
out.flush();
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
导入时&#xff0c;因为在我们的excel表中所给出的民族name&#xff0c;政治面貌name等&#xff0c;但我们需要获取到对应的民族id&#xff0c;政治面貌id&#xff0c;职称id&#xff0c;职位id等。有两种方法
我们选择第二种方法实现
&#64;ApiOperation(value &#61; "导入员工数据")
&#64;PostMapping("/import")
public RespBean importEmployee(MultipartFile file){
//创建导入对象
ImportParams params &#61; new ImportParams();
//去掉标题第一行
params.setTitleRows(1);
/**
* 导入 流的形式、POJO对象类的字节码、数据--- 返回的是数据
*
* 导入完成后&#xff0c;pojo.Employee-nation是有值的
* pojo.nation拿到的就是name 是因为&#xff1a;
* Excel注解&#xff0c;ExcelEntiy注解&#xff1a;
* 能通过汉族名字获取到它是nation.ExcelEntiy就知道了是对象里面的&#xff0c;从而得到name
* 如何获得name的id呢&#xff1f;
*
* for循环
* 重写了hashcode方法&#xff0c;也准备了有参构造。
* 1.从excel导入的数据中&#xff0c;拿到了nation对象&#xff0c;对象里面有name&#xff0c;id是空的
* 2.查询所有的nationList,获取对象索引下标&#xff1a;重写了equals和hashcode&#xff0c;
* 通过name名字去nationList比较。能获取一样的对象.从而得到下标。
* 3.通过nationList.get(下标).getId() 获取完整的对象&#xff1a;有id&#xff0c;name\
* 4.在从id&#xff0c;name中获取id。 将id放入到employee中。
*
* if插入&#xff1a;saveBatch批量插入。 插入集合
*
*/
List<Nation> nationlList &#61; nationService.list();
List<PoliticsStatus> politicsStatusList &#61; politicsStatusService.list();
List<Department> departmentList &#61; departmentService.list();
List<Joblevel> joblevelList &#61; joblevelService.list();
List<Position> positionList &#61; positionService.list();
try{
List<Employee> list &#61; ExcelImportUtil.importExcel(file.getInputStream(), Employee.class, params);
list.forEach(employee -> {
//民族id
employee.setNationId(nationlList.get(nationlList.indexOf(
new Nation(employee.getNation().getName()))).getId());
//政治面貌id
employee.setPoliticId(politicsStatusList.get(politicsStatusList.indexOf(
new PoliticsStatus(employee.getPoliticsStatus().getName()))).getId());
//部门id
employee.setDepartmentId(departmentList.get(departmentList.indexOf(new
Department(employee.getDepartment().getName()))).getId());
//职称id
employee.setJobLevelId(joblevelList.get(joblevelList.indexOf(new
Joblevel(employee.getJoblevel().getName()))).getId());
//职位id
employee.setPosId(positionList.get(positionList.indexOf(new
Position(employee.getPosition().getName()))).getId());
});
if (iEmployeeService.saveBatch(list)){
return RespBean.success("导入成功!");
}
return RespBean.error("导入失败!");
} catch (Exception e){
e.printStackTrace();
}
return RespBean.error("导入失败!");
}