EasyPoi实现Excel导出、导入
下面这种方式不需要模板,更加方便但是不能进行复杂的导出
一、引入依赖
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-spring-boot-starter</artifactId>
<version>4.4.0</version>
</dependency>
二、数据准备
三、数据库数据导出到Excel
3.1 创建数据表对应的BO类
/**
菜品
*/
@Data
public class Dish implements Serializable {
private static final long serialVersionUID = 1L;
@Excel(name = "菜品ID", width = 20) // 此注解标识的属性将被导出(该注解的各属性将在最后说明)
@ExcelIgnore // 此注解标识的属性将被忽略导出
private Long id;
//菜品名称
@Excel(name = "菜品名称", width = 20)
private String name;
//菜品分类id
@Excel(name = "菜品分类ID", width = 20)
private Long categoryId;
//菜品价格
@Excel(name = "菜品价格", width = 20)
private BigDecimal price;
//商品码
@Excel(name = "商品码", width = 20)
private String code;
//图片
@Excel(name = "菜品图片", width = 20)
private String image;
//描述信息
@Excel(name = "描述信息", width = 20)
private String description;
//0 停售 1 起售
private Integer status;
//顺序
private Integer sort;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createUser;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateUser;
//是否删除
private Integer isDeleted;
}
3.2 数据访问层实现
@Mapper
public interface DishDAO {
List<Dish> selectAllDish();
}
<?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.wyw.learn.excel.DishDAO">
<select id="selectAllDish" resultType="com.wyw.learn.excel.Dish">
select id,
name,
category_id as categoryId,
price,
code,
image,
description,
status,
sort,
create_time as createTime,
update_time as updateTime,
create_user as createUser,
update_user as updateUser,
is_deleted as isDeleted
from dish;
</select>
</mapper>
3.3 业务逻辑层实现(关键导出逻辑)
/**
* @author name: silk
* @version 1.0
* @description: TODO
* @date 2024/4/16 15:16
*/
@Service
public class DishService {
@Autowired
private DishDAO dishDAO;
/***
* @description: 测试导出数据至Excel
* @param:
* @return:
* @author name silk
* @date: 2024/4/16 15:01
*/
public void testDishExportExcel() throws Exception {
// 1. 查询数据库所有的菜品信息
List<Dish> dishList = dishDAO.selectAllDish();
// 2. 构造工作簿
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
// ExportParams有多个构造器,根据需要选择用哪个
// "菜品信息表"是第1个表头名称,df.format(new Date())是第2个表头名称,"sheet1"是当前sheet名称
ExportParams exportParams = new ExportParams("菜品信息表", df.format(new Date()), "sheet1");
// 注意ExcelExportUtil工具类并不完美,可能还需要针对具体需求进一步完善
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, Dish.class, dishList);
// 3. 构造文件输出流,输出excel
FileOutputStream os = new FileOutputStream("E:/" + "testExportExcel-" + df.format(new Date()) + ".xlsx");
workbook.write(os); // 将工作簿写出
os.flush(); // 强制把缓冲区中的数据发送到目的地,确保数据的及时性
os.close(); // 关闭流
}
}
3.4 导出结果
查看Excel可知,仅导出了被@Excel标识的属性,对应的列名称就是配置的name值;
关于@Excel其他属性,在下面第五节中说明;
四、读取Excel数据至数据库表
4.1 BO类复用上述Dish类
4.2 数据访问层省略
因为我们的Excel是从该数据库表中导出的,为了避免再次插入相同的数据,导致数据库报错,在此仅将Excel读取到List中,接口返回该List即可,不再写表;
4.3 业务逻辑层(关键读取逻辑)
/**
* @author name: silk
* @version 1.0
* @description: TODO
* @date 2024/4/16 15:16
*/
@Service
public class DishService {
@Autowired
private DishDAO dishDAO;
/***
* @description: 测试读取Excel数据
* @param:
* @return:
* @author name silk
* @date: 2024/4/17 10:05
*/
public List<Dish> testDishImportExcel() throws Exception {
List<Dish> res = new ArrayList<>();
// 1. 构建文件输入流
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
FileInputStream fileInputStream = new FileInputStream("E:/" + "testExportExcel-" + df.format(new Date()) + ".xlsx");
// 2. 构造工作簿,逐行逐列读取数据
XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
// 获取工作簿sheet1中的所有数据
XSSFSheet sheet1 = workbook.getSheet("sheet1");
// 排除表头信息,逐行读取
int lastRowNum = sheet1.getLastRowNum();
if (lastRowNum <= 2) {
System.out.println("Excel表有效数据为空,请重新导入!");
throw new Exception("Excel表有效数据为空,请重新导入!");
}
for (int i = 3; i < lastRowNum; i++) {
//行数据
XSSFRow row = sheet1.getRow(i);
if (ObjectUtil.isEmpty(row)) {
continue;
}
Dish dish = new Dish();
// 6列数据类型,(菜品名称 菜品分类ID 菜品价格 商品码 菜品图片 描述信息)
// 该行的第1个元素 菜品名称
XSSFCell name = row.getCell(0);
if (ObjectUtil.isEmpty(name) || StrUtil.isEmpty(name.toString())) {
continue;
}
dish.setName(name.toString());
// 该行的第2个元素 菜品分类ID
XSSFCell categoryId = row.getCell(1);
if (ObjectUtil.isEmpty(categoryId) || StrUtil.isEmpty(categoryId.toString())) {
continue;
}
dish.setCategoryId(Long.parseLong(categoryId.toString()));
XSSFCell price = row.getCell(2);
if (ObjectUtil.isEmpty(price) || StrUtil.isEmpty(price.toString())) {
continue;
}
// 需要注意数据类型转换,转换成适配的数据类型
dish.setPrice(BigDecimal.valueOf(Double.parseDouble(price.toString())));
XSSFCell code = row.getCell(3);
if (ObjectUtil.isEmpty(code) || StrUtil.isEmpty(code.toString())) {
continue;
}
dish.setCode(code.toString());
XSSFCell image = row.getCell(4);
if (ObjectUtil.isEmpty(image) || StrUtil.isEmpty(image.toString())) {
continue;
}
dish.setImage(categoryId.toString());
XSSFCell description = row.getCell(5);
if (ObjectUtil.isEmpty(description) || StrUtil.isEmpty(description.toString())) {
continue;
}
dish.setDescription(description.toString());
// 3. 数据添加至返回结果List,接口将该List返回做展示
res.add(dish);
}
return res;
}
}
4.4 读取结果
五、@Excel注解属性【参考博客】
EasyPoi的@Excel注解具有多个属性,下表总结了这些属性及其作用。
请注意,随着EasyPoi版本的更新,可能会有新的属性加入或旧的属性被修改,以下信息基于EasyPoi的一个特定版本,可能不涵盖所有的属性和最新的变动。