当前位置:网站首页>EasyExcel implements dynamic column parsing and table storage
EasyExcel implements dynamic column parsing and table storage
2022-08-03 16:40:00 【Listen to the wind, listen to the rain, listen to the world】
背景
一个表中的数据来源于多个其他系统的导出表,其中的特点就是大多数的字段都是一样的(可能导出的表头不一样),只有部分少数字段是每个系统自己独有的.围绕这个做一次功能性分析
分析:大多数字段是一样的,那么就是实际的表字段,唯一的区别就是各系统内的名字可能不一样,少数每个系统独有的字段,可以归为动态字段.
总结:
公共字段(翻译表头:
@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
组件来进行导入,实现公共列和动态列组合类型的导入,以及如何存储的功能,主要利用反射和字典分别来维护公共列和动态列的表头和字段的对应关系,利用此关系对数据进行解析.
边栏推荐
- C专家编程 第3章 分析C语言的声明 3.1 只有编译器才会喜欢的语法
- Big guys.Use flink-cdc-sqlserver version 2.2.0 to read sqlserver2008R
- C专家编程 第1章 C:穿越时空的迷雾 1.7 编译限制
- C专家编程 第2章 这不是Bug,而是语言特性 2.4 少做之过
- uniapp隐藏导航栏和横屏显示设置
- SQL中对 datetime 类型操作
- Some optional strategies and usage scenarios for PWA application Service Worker caching
- Huawei, Lenovo, BAIC, etc. were selected as the first batch of training bases for "Enterprise Digital Transformation and Security Capability Improvement" by the Ministry of Industry and Information Te
- yolov5s用自己的数据集进行训练模型
- Excuse me this hologres dimension table is cached?How to Finished
猜你喜欢
可复现、开放科研、跨学科合作:数据驱动下的科研趋势及应用方案
Components of communication - the drop-down menu
ORACLE CLOUD 在国内有数据中心吗?
Windows 事件转发到 SQL 数据库
TCP 可靠吗?为什么?
To add digital wings to education, NetEase Yunxin released the overall solution of "Internet + Education"
[Unity Getting Started Plan] Basic Concepts (6) - Sprite Renderer Sprite Renderer
2年开发经验去面试,吊打面试官,即将面试的程序员这些笔记建议复习
node connection mongoose database process
使用uniapp 封装一个request 请求
随机推荐
Big guys.Use flink-cdc-sqlserver version 2.2.0 to read sqlserver2008R
QT QT 】 【 to have developed a good program for packaging into a dynamic library
socket快速理解
生产环境如何删除表呢?只能在SQL脚本里执行 drop table 吗
想进阿里?先来搞懂一下分布式事务
leetcode:189. 轮转数组
带你了解什么是 Web3.0
Understand the recommendation system in one article: Outline 02: The link of the recommendation system, from recalling rough sorting, to fine sorting, to rearranging, and finally showing the recommend
C语言03、数组
TCP 可靠吗?为什么?
How to analyze the weekly activity rate?
C专家编程 第3章 分析C语言的声明 3.2 声明是如何形成的
C专家编程 第1章 C:穿越时空的迷雾 1.7 编译限制
TiKV & TiFlash accelerate complex business queries丨TiFlash application practice
MobileVIT实战:使用MobileVIT实现图像分类
关于oracle表空间在线碎片整理
中小微企业如何简单便捷、低成本实现数字化?360视觉云有妙招
组件通信--下拉菜单案例
MarkDown常用代码片段和工具
node connection mongoose database process