综述
本篇介绍读映射,分为两类映射。
概念映射:Easy excel框架与excel文件之间的映射关系
- ExcelReaderBuilder,ReadWorkBook 对应Excel文件
- ExcelReaderSheetBuilder, ReadSheet,对应sheet页
- Cell对应单元格, CellExtra对应附加信息,例如超链接,注释,标注等
- API分析
实体映射:excel数据与实体类之间的映射关系。
- 类层级,无对应,一对一,一对多个实体,一对同一实体集合。
- 字段层级
- 值层级
概念映射
默认情况下,一般一次读取只读取一个Excel文件。
一个Excel文件拥有多个sheet页,与它对应的对象为ReadWorkBook。
一个Sheet页拥有多个单元格,与它对应的对象为ReadSheet。
一个单元格拥有数据内容和附加信息,与它对应的对象为Cell接口。
附加信息,例如标注,注释,超链接等等,与它对应的对象为CellExtra,CellExtraTypeEnum包含附加信息的类型。
Easy Excel使用Builder来构建彼此之间的关系,EasyExcel调用read,返回ExcelReaderBuilder对象,它自动注入ReadWorkBook对象,调用sheet方法,返回ExcelReaderSheetBuilder,调用doRead时,每个单元格的内容对应ReadCell,若有附加信息,对应CellExtra。
2.1 ReadWorkBook
ReadWorkBook没有操作,它的属性由两部分组成
第一部分,它自身的属性。
第二部分,表单的公共属性,可以单独设置sheet页的属性,统一设置更方便。
2.1.1 自身属性
使用EasyExcel.read方法,它返回ExcelReaderBuilder对象,使用它设置属性
Excel文件相关
// 对应excelType属性,CSV, XLSX,XLS
readerBuilder.excelType(ExcelTypeEnum.XLSX);
// 对应inputStream,file属性, 三种参数类型,filePath, File对象,InputStream对象
String filePath ="F:\\Learn\\LearnEasyExcel\\carInfo.xlsx";
readerBuilder.file(filePath);
// 对应charset属性
readerBuilder.charset(Charset.defaultCharset());
// 密码,对应password属性
readerBuilder.password("xx");
读取行为
// 忽略空白行
readerBuilder.ignoreEmptyRow(true);
// 对应extraReadSet, 有超链接,注释,标注三种
readerBuilder.extraRead(CellExtraTypeEnum.HYPERLINK);
// 返回的数据类型,用String接就可以,不然会内部进行类型转换,报错就没有后续操作了。
readerBuilder.readDefaultReturn(ReadDefaultReturnEnum.String);
// 对应autoCloseStream
readerBuilder.autoCloseStream(true);
// 读取缓存,超出5M时使用缓存,缓存对象类型需要相关的缓存框架支持
readerBuilder.readCache(new MapCache());
杂项
// 对应mandatoryUseInputStream属性
readerBuilder.mandatoryUseInputStream(false);
// 缓存的选择器
readerBuilder.readCacheSelector(new SimpleReadCacheSelector());
// 对应xlsxSAXParserFactoryName
readerBuilder.xlsxSAXParserFactoryName("");
// 对应useDefaultListener
readerBuilder.useDefaultListener(true);
2.1.2 Sheet公共属性
获取Sheet表单
// 只有一个sheet页
readerBuilder.sheet();
// 指定某个sheet页,位置
readerBuilder.sheet(0);
// 指定某个sheet页,名称
readerBuilder.sheet("sheet_name");
// 二者的结合
readerBuilder.sheet(0, "sheet_name");
标题
// 带标题
sheetBuilder.head(Car.class);
// 指定标题占的行数
sheetBuilder.headRowNumber(1);
转换器和监听器
// 注册类型转换器
sheetBuilder.registerConverter();
// 注册监听器
sheetBuilder.registerReadListener();
杂项
// 是否对数据进行trim
sheetBuilder.autoTrim(true);
// 时间保存的是数字,默认开始时间为1990年,设置该值,开始时间为1904年
sheetBuilder.use1904windowing(false);
// 是否使用科学计数法
sheetBuilder.useScientificFormat(true);
2.2 ReadSheet
通过sheetNo,名称获取sheet, 在ExcelReaderBuilder统一为sheet方法,ExcelReaderSheetBuilder使用的sheetNo,sheetName。
// sheet位置,从0开始
sheetBuilder.sheetNo(0);
// sheet名称
sheetBuilder.sheetName("sheet_name");
2.3 API分析
官网提供的读有四种写法。以第三种写法为例。EasyExcel.read(filename,class, listener).sheet().doRead()。它由三部分组成,
第一部分, read,设置workBook的属性,注册监听器等等。
第二部分,sheet, 获取某个sheet页。
第三部分,读取,两种方式doRead,或者doReadSync。
所有写法的组成结构都是相似的。
第一种,pageReadListener提供了分页功能,感觉没必要。EasyExcel.read(filename, class, pageReadListener).sheet().doRead();
第二种,匿名内部类的方式,通常不合适,阅读性较差。EasyExcel.read(filename, class, innerListener).sheet().doRead();
第三种,最常用的方式。EasyExcel.read(filename, class, listener).sheet().doRead();
第四种,抽离sheet页,指定某个sheet页,没必要,直接sheet(number)即可。
ExcelReader reader = EasyExcel.read(filename, class, listener).doRead();
// 第几个sheet页。
ReadSheet readSheet = EasyExcel.readSheet(index).build();
reader.read(readSheet);
实体映射
实体映射包含三个层级。
类层级,一条数据对应1到N个实体对象。
字段层级,一个字段对应一到N个单元格。
字段值层级,字段值的校验,转换,格式化。
3.1 类层级
无具体的实体类,只有数据,实体类变为了Map,每一行的数据放入到该Map中, Integer为列的顺序值,String为单元格的内容。
一条记录对一个实体,最简单,最常见。指定标题对应的Class即可。sheetBuilder.head(Car.class);
一条记录对应N个实体。思路如下:
第一步,使用1对1的关系,使用All接收。
第二步,通过BeanUtils拷贝同名属性,将All对象分裂为多个。
// 定义ExcelDTO
public class TestExcelDTO{
// 隶属于Test类
@ExcelProperty(index = 0)
private String id;
// 隶属于TestExt类
@ExcelProperty(index = 1)
private String extProp;
}
// Test类
public class Test{
private String id;
}
// TestExt类
public class TestExt{
private String extProp;
}
// 解析完成之后,调用copyProperties方法,Apache common下的BeanUtils不会拷贝同名但类型不同的属性。
BeanUtils.copyProperties(test, testExcelDTO);
BeanUtils.copyProperties(testExt, testExcelDTO);
一条记录对应一个实体的集合。通常是复杂表格,需要设置headRowNumber,思路如下:
第一步,自定义Group注解
第二步,使用1对1的关系,在该对象中使用Group注解,将其分组。
第三步,解析该对象,将拥有相同Group的字段存放在JSONObject中,
第四步,将这些JSONObject放入JSONArray中,将JSONArray转换为List
// 定义Group注解
public @interface Group{
String value() default "";
}
// 定义ExcelDTO
public class TestExcelDTO{
// 第一组
@ExcelProperty(index = 0)
@Group("one")
private String idOne;
// 第二组
@ExcelProperty(index = 0)
@Group("two")
private String idTwo;
}
// 第三步,转换为Map<String, JSONObject>, key为组名称, value为该组中的每个对象,此处"one":{"id":"1"}; "two":{"id":"2"}
// 第四步,将这些对象都放入JSONObject中
ListIterator<JSONObject> listIterator = jsonArray.listIterator();
while(listIterator.hasNext()){
// 转换为对象
JSONObject current = iterator.next();
Test test = current.toJavaObject(Test.class);
// 后续保存test对象操作
}
3.2 字段层级
通常一列对应一个字段,字段映射有两种方式。
标题名称,在@ExcelProperty添加value属性。
列的顺序,在@ExcelProperty添加index属性。从0开始。
可以包含和排除某个字段,有两种策略,
- 默认不包含所有字段,只对添加@ExcelProperty的生效,需要在类上添加@ExcelIgnoreUnannotated。
- 默认包含所有字段,排除某个字段,需要在字段上添加@ExcelIgnore。
3.3 值层级
单元格的内容,转换为字段的值。需要进行校验,类型转换,格式化。
单元格的附加内容,转换为CellExtra对象,可以获取该对象的属性转换为字段的属性。例如超链接信息。