当前位置:网站首页>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
组件来进行导入,实现公共列和动态列组合类型的导入,以及如何存储的功能,主要利用反射和字典分别来维护公共列和动态列的表头和字段的对应关系,利用此关系对数据进行解析.
边栏推荐
猜你喜欢
node连接mongoose数据库流程
MySQL窗口函数
DAYU200 OpenHarmony标准系统HDMI全屏显示
[Unity Getting Started Plan] Basic Concepts (6) - Sprite Renderer Sprite Renderer
C专家编程 第1章 C:穿越时空的迷雾 1.6 它很棒,但它符合标准吗
华为、联想、北汽等入选工信部“企业数字化转型和安全能力提升”首批实训基地
[Deep Learning] Today's bug (August 2)
虹科分享 | 如何测试与验证复杂的FPGA设计(3)——硬件测试
罗克韦尔AB PLC RSLogix5000中创建新项目、任务、程序和例程的具体方法和步骤
Web3 安全风险令人生畏?应该如何应对?
随机推荐
2年开发经验去面试,吊打面试官,即将面试的程序员这些笔记建议复习
纯纯粹粹纯纯粹粹
EA 改口,称单人游戏是产品组合中“非常重要的一部分”
产品-Axure9英文版,轮播图效果
MySQL相关介绍
C语言02、语句、函数
MPLS的wpn实验
DataGrip数据仓库工具
vector类
SwinIR实战:如何使用SwinIR和预训练模型实现图片的超分
Kubernetes 笔记 / 生产环境
最强分布式锁工具:Redisson
To add digital wings to education, NetEase Yunxin released the overall solution of "Internet + Education"
兄弟组件通信context
面了个腾讯35k出来的,他让我见识到什么叫精通MySQL调优
附录A 程序员工作面试的秘密
甲方不让用开源【监控软件】?大不了我自己写一个
protobuf 反射使用总结
【带你了解SDN和网络虚拟化】
Components of communication - the drop-down menu