在使用EasyExcel进行Excel导入时,我们经常需要将Excel中的数据映射到Java实体类中。如果Excel的列名是固定的,我们可以通过@ExcelProperty("列名")注解直接在实体类中指定列名。但如果Excel的列名不固定,或者我们希望根据Excel的第一行来动态确定映射关系,我们就需要一种更灵活的方法。本教程将介绍如何使用EasyExcel创建一个工具类,以支持动态映射Excel数据到任意实体类。
步骤1:添加EasyExcel依赖
首先,确保你的项目中已经添加了EasyExcel的依赖。如果使用Maven,可以在pom.xml
中添加如下依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.x.x</version> <!-- 使用最新版本 -->
</dependency>
步骤2:创建动态映射工具类
创建一个工具类DynamicExcelUtil
,它将负责读取Excel文件,并根据第一行的列名动态映射数据到实体类。
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import lombok.var;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class ExcelUtil {
/**
* 读取 Excel 文件并返回数据模型列表。
*
* @param fileName Excel 文件的路径。
* @param clazz 数据模型类。
* @param <T> 数据模型的类型。
* @return 数据模型列表。
*/
public static <T> List<T> readExcel(String fileName, Class<T> clazz) {
ExcelDataListener<T> listener = new ExcelDataListener<>();
List<T> dataModels = new ArrayList<>();
try {
EasyExcel.read(fileName, clazz, listener).sheet().doRead();
dataModels = listener.getDataModels();
} catch (Exception e) {
e.printStackTrace();
}
return dataModels;
}
/**
* Excel 数据读取监听器。
*
* @param <T> 数据模型的类型。
*/
public static class ExcelDataListener<T> implements ReadListener<T> {
private List<T> dataModels = new ArrayList<>();
@Override
public void invoke(T dataModel, AnalysisContext context) {
dataModels.add(dataModel);
}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 所有数据解析完成后的操作
}
public List<T> getDataModels() {
return dataModels;
}
}
/**
* 将 MultipartFile 转换为 File 对象。
*
* @param file MultipartFile 对象。
* @return 转换后的 File 对象。
* @throws IOException 如果文件写入失败。
*/
public static File convert(MultipartFile file) throws IOException {
if (!file.isEmpty()) {
File convFile = new File(Objects.requireNonNull(file.getOriginalFilename()));
try (var fos = new FileOutputStream(convFile)) {
fos.write(file.getBytes());
}
return convFile;
}
throw new IOException("Could not convert the uploaded file.");
}
}
如果我们现在有一个实体类,如下:
package com.xiaobai.easyexceldemo.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;
/**
*
* @TableName easy_demo
*/
@TableName(value ="easy_demo")
@Data
public class EasyDemo implements Serializable {
/**
*
*/
@TableId
private Integer id;
/**
*
*/
private String name;
/**
*
*/
private String password;
/**
*
*/
private String email;
/**
*
*/
private String qq;
/**
*
*/
private String address;
/**
*
*/
private String nameSpace;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
}
此时我们我们有一个表格,数据如下:
测试:方式一,直接映射
此时我们映射的时候,需要按照表格的列新创建一个中间类。
@Data
public class EasyDemoDto {
/**
*
*/
private String name;
/**
*
*/
private String password;
/**
*
*/
private String email;
/**
*
*/
private String qq;
/**
*
*/
private String address;
/**
*
*/
private String nameSpace;
}
这个类需要按照表格的列进行按照顺序排列,才可以映射上。
现在我们调换一下(name,password)字段的顺序,看一下结果:
所以直接映射的时候一定要注意,字段和excel表格列顺序一致
测试:方式二,注解映射
在实体中添加注解@ExcelProperty("列名"),映射excel表中的列名,进行映射数据。
@Data
public class EasyDemoDto {
/**
*
*/
@ExcelProperty("密码")
private String password;
/**
*
*/
@ExcelProperty("名称")
private String name;
/**
*
*/
@ExcelProperty("邮箱")
private String email;
/**
*
*/
@ExcelProperty("qq")
private String qq;
/**
*
*/
@ExcelProperty("地址")
private String address;
/**
*
*/
@ExcelProperty("空间")
private String nameSpace;
}
EasyExcel中注解说明
@ExcelProperty
: 用于指定实体类属性与 Excel 列的映射关系。
public class Student {
@ExcelProperty("学生姓名")
private String name;
// 其他属性和getter/setter方法
}
@DateTimeFormat
: 用于指定日期时间格式,与@ExcelProperty
一起使用。
@ExcelProperty("出生日期")
@DateTimeFormat("yyyy-MM-dd")
private Date birthDate;
@NumberFormat
: 用于指定数字格式,与@ExcelProperty
一起使用。
@ExcelProperty("分数")
@NumberFormat("#.##")
private double score;
@ColumnWidth
: 用于指定列宽。
public class Product {
@ExcelProperty("产品名称")
@ColumnWidth(20)
private String name;
// 其他属性和getter/setter方法
}
@ContentFontStyle
: 用于定义内容单元格的字体样式。
@ExcelProperty("备注")
@ContentFontStyle(name = "微软雅黑", size = 12, isBold = true)
private String comment;
@ContentLoopMerge
: 用于在循环写入时合并单元格。
public class SalesRecord {
@ExcelProperty("产品名称")
@ContentLoopMerge(startRow = 1, endRow = 3)
private String productName;
// 其他属性和getter/setter方法
}
@ContentRowHeight
: 用于设置内容行高。
@ExcelProperty("详细描述")
@ContentRowHeight(value = 50)
private String description;
@ContentStyle
: 用于定义内容单元格的样式。
@ExcelProperty("数值")
@ContentStyle(fillForegroundColor = 3, fillPatternType = FillPatternType.SOLID_FOREGROUND)
private double value;
@HeadFontStyle
: 用于定义表头单元格的字体样式。
public class Employee {
@ExcelProperty("工号")
@HeadFontStyle(name = "宋体", size = 14, isBold = true)
private String employeeId;
// 其他属性和getter/setter方法
}
@HeadRowHeight
: 用于设置表头行高。
@ExcelProperty("姓名")
@HeadRowHeight(value = 30)
private String name;
@HeadStyle
: 用于定义表头单元格的样式。
@ExcelProperty("部门")
@HeadStyle(fillForegroundColor = 4, fillPatternType = FillPatternType.SOLID_FOREGROUND)
private String department;
@OnceAbsoluteMerge
: 用于绝对合并单元格一次。
public class Summary {
@ExcelProperty("总计")
@OnceAbsoluteMerge(merge = {@ExcelColIndex(1), @ExcelColIndex(2)}, row = 1)
private double total;
// 其他属性和getter/setter方法
}
@ExcelIgnore
: 用于忽略不映射到 Excel 的实体类属性。
private String internalUseOnly;
@ExcelIgnoreUnannotated
: 类级别的注解,指定只有使用了@ExcelProperty
注解的字段才会写入 Excel。
@ExcelIgnoreUnannotated
public class Product {
private String id;
@ExcelProperty("产品名称")
private String name;
// 只有name属性会写入Excel
}
@ExcelColIndex
: 用于指定列的索引。
@ExcelProperty("价格")
@ExcelColIndex(2)
private double price;
至此,已经结束啦,获取有不对的地方,还请提出,共勉。