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 mapperPUBLIC "-//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 isDeletedfrom dish;</select>
</mapper>
3.3 业务逻辑层实现(关键导出逻辑)
/*** @author name: silk* @version 1.0* @description: TODO* @date 2024/4/16 15:16*/
@Service
public class DishService {@Autowiredprivate 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. 构造文件输出流,输出excelFileOutputStream 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 {@Autowiredprivate 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个元素 菜品分类IDXSSFCell 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的一个特定版本,可能不涵盖所有的属性和最新的变动。