EasyExcel实现多级表头(间隔表头)的导入操作,通过自定义监听器实现

一、引言

这个需求其实很简单,需求就是将一条数据导入数据库实现替换,只不过有很多属性,单独一行进行编辑比较繁琐,所以模板定为如图所示,将其分段进行编辑。

一开始我以为有现成的方法可以调用,试着搜索多级表头的操作,结果和我想象的不太一样,实际逻辑还是一层表头,下面都是数据行了。所以我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层后可以操作返回了的对象进行后面的业务逻辑操作,最好不要在监听器里面写完后续的处理逻辑,减少耦合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值