java EasyExcel实现动态列解析和存表

 更新时间:2023年06月15日 17:28:32   作者:我是一颗小虎牙_  
这篇文章主要为大家介绍了java EasyExcel实现动态列解析和存表示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

背景

一个表中的数据来源于多个其他系统的导出表,其中的特点就是大多数的字段都是一样的(可能导出的表头不一样),只有部分少数字段是每个系统自己独有的。围绕这个做一次功能性分析

分析:大多数字段是一样的,那么就是实际的表字段,唯一的区别就是各系统内的名字可能不一样,少数每个系统独有的字段,可以归为动态字段。

总结:

  • 公共字段(翻译表头:@ExcelProperty 可以指定多个表头( @ExcelProperty(value = {"发货数量", "采购数量(台)"}) ))
  • 动态字段(需要有每个系统内动态字段的字段名称和表头的对应关系,考虑使用字典,供业务员配置,后续如果新添加其他动态字段直接在字典中配置,无需另行开发)

注意:由于无法控制和预料固定字段在新接入的系统中的实际表头,所以如果新接入系统的公共表头与表字段不一致,需要在 @ExcelProperty(value = {}) 中添加新的表头

效果

字典配置:

数据表结果:

公共字段使用常规的数据库表字段存储,动态字段使用额外列存 JSON 串。

代码

引入pom坐标

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.0</version>
</dependency>

创建实体类

public class AgentDeliverOrderImportVo {
    @ExcelProperty(value = {"订单编号"}, order = 1)
    private String deliverNo;
    @ExcelProperty(value = {"发货数量", "采购数量(台)"}, order = 14)
    @ColumnName(name = {"发货数量", "采购数量(台)"})
    private Integer deliverCount;
    /**
     * 动态字段(业务线编号区分)
     */
    private String dynamicFields;
    private Date createTime;
    private String createBy;
}
  • 因为存在不确定的列,所以只能使用 EasyExcel 的不创建对象的写,那么
public String test(MultipartFile file) throws IOException {
    //假设从字典中获取字典值
    Map<String, String> dictMap = new HashMap<>();
    dictMap.put("项目", "xm");
    dictMap.put("嗨一付订单编号", "hyfddbh");
    try (InputStream inputStream = file.getInputStream()) {
        EasyExcel.read(inputStream, new ReadListener<Map<String, String>>(){
            private Map<Integer, String> fieldHead;
            //获取表头
            @Override
            public void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {
                Map<Integer, String> integerStringMap = ConverterUtils.convertToStringMap(headMap, context);
                log.info("解析到一条头数据:{}", JSON.toJSONString(integerStringMap));
                fieldHead = ExcelParsing.setFieldHead(integerStringMap, AgentDeliverOrderImportVo.class);
                log.info("转化后头数据:{}", JSONObject.toJSONString(fieldHead));
            }
            //获取数据
            @Override
            public void invoke(Map<String, String> map, AnalysisContext analysisContext) {
                log.info("解析到一条数据:{}", JSON.toJSONString(map));
                Map<String, String> valueMap = ExcelParsing.setFieldValue(fieldHead, dictMap, map);
                log.info("转化一条数据:{}", JSONObject.toJSONString(valueMap));
                log.info("转化一条动态数据:{}", JSONObject.toJSONString(ExcelParsing.getValueMap(valueMap, AgentDeliverOrderImportVo.class)));
            }
            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            }
        }).sheet().doRead();
    }
    return "完成";
}
/**
 * @author Surpass
 * @Description: excel处理类
 * @date 27/07/2022 15:04
 */
class ExcelParsing {
    /**
     * 将公共字段中的中文转换成数据库表字段,动态字段(其他字段保留)
     * @param headMap               {1:"姓名", 2:"年龄"}
     * @param obj                   AgentDeliverOrderImportVo(导入实体类)
     * @return java.util.Map<java.lang.String, java.lang.String>       {1:"name", 2:"年龄"}
     * @author Surpass
     * @date 01/08/2022 17:10
     */
    public static Map<Integer, String> setFieldHead(Map<Integer, String> headMap, Class<?> obj) {
        Field[] fields = obj.getDeclaredFields();
        for (Field field : fields) {
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation == null) {
                continue;
            }
            //存在翻译字段的情况,一个字段对应好几个表头(尽量避免)
            List<String> valueList = Arrays.asList(annotation.value());
            for (Map.Entry<Integer, String> entry : headMap.entrySet()) {
                if (valueList.contains(entry.getValue())) {
                    headMap.put(entry.getKey(), field.getName());
                }
            }
        }
        return headMap;
    }
    /**
     * 获取数据(平铺),指动态字段kv和公共字段kv在同一级
     * @param headMap               {1:"name", 2:"年龄"}
     * @param dictMap               {"年龄":"age"}
     * @param valueMap              {1:"广州****公司", 2:"23"}
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @author Surpass
     * @date 01/08/2022 17:10
     */
    public static Map<String, String> setFieldValue(Map<Integer, String> headMap,
                                                    Map<String, String> dictMap,
                                                    Map<String, String> valueMap) {
        Map<Integer, String> valueIntegerMap = valueMap.entrySet().stream().collect(
                Collectors.toMap(item -> Integer.valueOf(String.valueOf(item.getKey())),
                        item -> StrUtil.nullToEmpty(item.getValue()))
        );
        Map<String, String> valueResultMap = new HashMap<>(valueMap.size());
        Iterator<Map.Entry<Integer, String>> iterator = valueIntegerMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> entry = iterator.next();
            //动态字段
            if (dictMap != null && dictMap.containsKey(headMap.get(entry.getKey()))) {
                valueResultMap.put(dictMap.get(headMap.get(entry.getKey())), entry.getValue());
                continue;
            }
            //公共字段
            valueResultMap.put(headMap.get(entry.getKey()), entry.getValue());
            iterator.remove();
        }
        return valueResultMap;
    }
    /**
     * 获取数据(表结构),指动态字段kv已经加入到数据库表字段 dynamicFields 中
     * @param obj                   AgentDeliverOrderImportVo(导入实体类)
     * @param valueMap              {"name":"广州****公司", "age":"23"}
     * @return java.util.Map<java.lang.String, java.lang.String>  
     * 返回结果: {"name":"广州****公司","dynamicFields":{"age":"23"}}
     * @author Surpass
     * @date 01/08/2022 17:10
     */
    public static Map<String, Object> getValueMap(Map<String, String> valueMap,
                                                  Class<?> obj) {
        Map<String, Object> resultMap = new HashMap<>(valueMap);
        List<String> commonFieldList = new ArrayList<>();
        Field[] fields = obj.getDeclaredFields();
        for (Field field : fields) {
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation == null) {
                continue;
            }
            commonFieldList.add(field.getName());
        }
        //过滤掉实体中的公共字段
        Map<String, String> dynamicMap = valueMap.entrySet().stream()
                .filter(item -> !commonFieldList.contains(item.getKey()))
                .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        resultMap.put("dynamicFields", dynamicMap);;
        return resultMap;
    }
}

经过解析以后这个文档的数据已经和数据库表一致了,那么我们后续的操作就是常规的校验和插入逻辑了。

目前有一个缺点就是这样存的动态字段不好做条件查询,影响不是很大。

总结

本文介绍了使用 EasyExcel 组件来进行导入,实现公共列和动态列组合类型的导入,以及如何存储的功能,主要利用反射和字典分别来维护公共列和动态列的表头和字段的对应关系,利用此关系对数据进行解析。

以上就是java EasyExcel实现动态列解析和存表的详细内容,更多关于java EasyExcel动态列存表的资料请关注脚本之家其它相关文章!

相关文章

  • Java调用本地库的JNA快速入门教程

    Java调用本地库的JNA快速入门教程

    JNA(Java Native Access)是Java库,简化调用操作系统API,本文通过一个示例项目展示了如何使用JNA与C/C++编写的DLL交互,包括定义原生接口、数据类型映射、指针和引用的处理、结构体的使用以及异常处理等,感兴趣的朋友一起看看吧
    2025-09-09
  • java连接Mysql数据库的工具类

    java连接Mysql数据库的工具类

    这篇文章主要介绍了java连接Mysql数据库的工具类,非常的实用,推荐给大家,需要的朋友可以参考下
    2015-03-03
  • 解决idea 项目编译后没有class文件的问题

    解决idea 项目编译后没有class文件的问题

    这篇文章主要介绍了解决idea 项目编译后没有class文件的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 2022版IDEA创建一个maven项目的超详细图文教程

    2022版IDEA创建一个maven项目的超详细图文教程

    IDEA是用于java语言开发的集成环境,并且经常用于maven、spring、MyBatis等项目的开发,下面这篇文章主要给大家介绍了关于2022版IDEA创建一个maven项目的超详细图文教程,需要的朋友可以参考下
    2023-02-02
  • Java中实现OCR识别读取图片中的文字

    Java中实现OCR识别读取图片中的文字

    图片内容一般无法编辑,如果想要读取图片中的文本,我们需要用到OCR工具,本文将介绍如何在Java中实现OCR识别读取图片中的文字,文中通过代码示例介绍的非常详细,需要的朋友可以参考下
    2024-04-04
  • Maven 的配置文件路径读取方法

    Maven 的配置文件路径读取方法

    这篇文章主要介绍了Maven 的配置文件路径读取方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • 详解Spring FactoryBean灵活创建复杂对象的秘密武器

    详解Spring FactoryBean灵活创建复杂对象的秘密武器

    FactoryBean是Spring框架中用于创建复杂Bean的接口,通过编程方式控制Bean的创建过程,它允许开发者自定义Bean的创建逻辑,适用于集成第三方库、延迟初始化、动态代理和统一管理资源等场景,本文介绍Spring FactoryBean创建复杂对象的相关操作,感兴趣的朋友一起看看吧
    2025-02-02
  • mybatis自动扫描和自定义类注解方式

    mybatis自动扫描和自定义类注解方式

    这篇文章主要介绍了mybatis自动扫描和自定义类注解方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • 在macOS上安装jenv管理JDK版本的详细步骤

    在macOS上安装jenv管理JDK版本的详细步骤

    jEnv是一个命令行工具,正如它的官网所宣称的那样,它是来让你忘记怎么配置JAVA_HOME环境变量的神队友,这篇文章主要介绍了在macOS上安装jenv管理JDK版本的详细步骤,需要的朋友可以参考下
    2025-07-07
  • IDEA Error:java: 无效的源发行版: 17错误

    IDEA Error:java: 无效的源发行版: 17错误

    本文主要介绍了IDEA Error:java: 无效的源发行版: 17错误,这个错误是因为您的IDEA编译器不支持Java 17版本,您需要更新您的IDEA编译器或者将您的Java版本降级到IDEA支持的版本,本文就来详细的介绍一下
    2023-08-08

最新评论