当前位置:网站首页>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 组件来进行导入,实现公共列和动态列组合类型的导入,以及如何存储的功能,主要利用反射和字典分别来维护公共列和动态列的表头和字段的对应关系,利用此关系对数据进行解析.
边栏推荐
- 高效的组织信息共享知识库是一种宝贵的资源
- 如何使用MATLAB绘制极坐标堆叠柱状图
- 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
- 元宇宙系列--Value creation in the metaverse
- MySQL窗口函数 PARTITION BY()函数介绍
- node connection mongoose database process
- 视频人脸识别和图片人脸识别的关系
- 详谈RDMA技术原理和三种实现方式
- 如何设计大电流九线导电滑环
- 简易网络传输方法
猜你喜欢
![[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 01](/img/8e/fcf79d150af4384c14a118fb209725.png)
[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 01

视频人脸识别和图片人脸识别的关系

面试突击71:GET 和 POST 有什么区别?
![[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 02](/img/45/96af4ca21329964808a4c8f2b8272c.png)
[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 02

罗克韦尔AB PLC RSLogix5000中创建新项目、任务、程序和例程的具体方法和步骤

Components of communication - the drop-down menu

如何使用MATLAB绘制极坐标堆叠柱状图

设置海思芯片MMZ内存、OS内存详解

leetcode SVM

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
随机推荐
关于oracle表空间在线碎片整理
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
error:Illegal instruction (core dumped),离线下载安装这个other版本numpy
MySQL相关介绍
[Unity Getting Started Plan] Basic Concepts (6) - Sprite Renderer Sprite Renderer
Kubernetes 笔记 / 任务 / 管理集群 / 用 kubeadm 管理集群 / 配置一个 cgroup 驱动
面试不再被吊打!这才是Redis分布式锁的七种方案的正确打开方式
产品-Axure9英文版,轮播图效果
2年开发经验去面试,吊打面试官,即将面试的程序员这些笔记建议复习
Which thread pool does Async use?
Auto Scaling 弹性伸缩(运维释放人力)
C语言04、操作符
【AppCube】零代码小课堂开课啦
FinClip | 2022 年 7 月产品大事记
MPLS的wpn实验
leetcode-268.丢失的数字
CopyOnWriteArrayList details
[redis] cache penetration and cache avalanche and cache breakdown solutions
smp,numa和mpp体系结构总结
请问下这个hologres维表是被缓存了么?怎么直接Finished了