一、引言
这个需求其实很简单,需求就是将一条数据导入数据库实现替换,只不过有很多属性,单独一行进行编辑比较繁琐,所以模板定为如图所示,将其分段进行编辑。
一开始我以为有现成的方法可以调用,试着搜索多级表头的操作,结果和我想象的不太一样,实际逻辑还是一层表头,下面都是数据行了。所以我i这里定义一下这种类似需求为间隔表头。
我这里只是抛砖引玉,也希望有更好的想法可以提出。
二、思路
我没有深扒源码(能力不够),所以不是很知道它的@ExcelProperty注解和转换器注解要如何调用,我这里只是简单看了一下监听器的实现。
EasyExcel.read(file.getInputStream(), MultiHeaderExcelListener).sheet().headRowNumber(1).doRead();
这里使用的read方法只有输入流和自定义监听器MultiHeaderExcelListener,中间如果尝试使用使用了注解@Excel Property注解的实体类.class的参数会报错,所以后续只能手写转换逻辑。
三、MultiHeaderExcelListener的实现
需要继承AnalysisEventListener<Map<Integer, String>>这个类
public class MultiHeaderExcelListener extends AnalysisEventListener<Map<Integer, String>>
需要重写invoke和doAfterAllAnalyysed这两个方法
@Override
public void invoke(Map<Integer, String> object, AnalysisContext context) {}
@Override
public void doAfterAllAnalysed(AnalysisContext context) {}
invoke方法是将excel表格中的数据一行一行的读取,所以我们可以对其中的每一行读取到的内容进行判断和处理。
当读取一行内容后,会将这一行内容自动转换为一个Map<Integer,String>,分别是表格内单元格的从左往右的从0开始的序号和单元格中的内容。
所以我这里的逻辑是将一行表头和对应的下一行数据都读取完后,rowCount++,循环这个操作。然后分别创建各自的汇总的allHeader和allData的Map<Integer,String>,并各自创建了一个headerSize和dataSize的map来存储每两行(表头+数据)的大小,来为下一个循环中将这一行的数据或表头插入总表头或总数据map中。
@Override
public void invoke(Map<Integer, String> object, AnalysisContext context) {
if (isHeader) {
// log.info("读取表头: {},", object.values());
if(rowCount==0){
object.forEach((k, v)->{
allHeader.put(k, v);
allHeaderSize++;
});
headerSize.put(rowCount, allHeaderSize);
}else{
object.forEach((k, v)->{
allHeader.put(k+headerSize.get(rowCount-1), v);
allHeaderSize++;
});
headerSize.put(rowCount, allHeaderSize);
}
isHeader = false;
} else {
// log.info("读取数据行: {}", object.values());
if(rowCount==0){
object.forEach((k, v)-> {
allData.put(k, v);
allDataSize++;
});
dataSize.put(rowCount, allDataSize);
}else{
object.forEach((k, v)->{
allData.put(k+dataSize.get(rowCount-1), v);
allDataSize++;
});
dataSize.put(rowCount, allDataSize);
}
rowCount++;
isHeader = true;
}
}
doAfterAllAnalysed方法是在处理完所有的表格内容后进行最后的处理,用于将表头的中文内容转换为对应的列名,并通过反射执行各自的set方法来为其赋值,并且同时可以对这列对应的数据进行转换。
ConvertName实际上也是我自定义的,没有深扒源码,这里应该可以用EasyExcel自带的Convert类中的convertToJava方法转换,我这里就是用最笨的Switch来手动替换,就不贴代码了。也希望有大佬可以教一下怎么使用哈哈。
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
Map<Integer, String> allHeader = this.allHeader;
Map<Integer, String> allData = this.allData;
modelSizeConfig = new ModelSizeConfig();
for (Integer i = 0; i < allHeader.size(); i++ ) {
if (allHeader.get(i) != null) {
String headerName = ConvertName(allHeader.get(i));
try {
if (allData.get(i).equals("无") || allData.get(i).equals("有") ||
allData.get(i).equals("正向") || allData.get(i).equals("反向") ||
allData.get(i).equals("0") || allData.get(i).equals("1")){
if (allData.get(i).equals("无") || allData.get(i).equals("反向") || allData.get(i).equals("0")){
allData.put(i, "0");
}else{
allData.put(i, "1");
}
Method method = ModelSizeConfig.class.getMethod("set" + headerName, Integer.class);
method.invoke(modelSizeConfig, Integer.parseInt(allData.get(i)));
}else {
Method method = ModelSizeConfig.class.getMethod("set" + headerName, Double.class);
method.invoke(modelSizeConfig, Double.parseDouble(allData.get(i)));
}
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("Error setting property: {}", headerName, e);
}
}
}
}
最后不要忘了添加一个get方法来返回你前面set了那么多的对象。
public ModelSizeConfig getModelSizeConfig() {
return modelSizeConfig;
}
因为最前面用的是doRead()的方法,所以回到Serviuce层后可以操作返回了的对象进行后面的业务逻辑操作,最好不要在监听器里面写完后续的处理逻辑,减少耦合。
5829

被折叠的 条评论
为什么被折叠?



