当前位置:网站首页>EasyExcel实现动态列解析和存表
EasyExcel实现动态列解析和存表
2022-08-03 16:24:00 【听风听雨听世界】
背景
一个表中的数据来源于多个其他系统的导出表,其中的特点就是大多数的字段都是一样的(可能导出的表头不一样),只有部分少数字段是每个系统自己独有的。围绕这个做一次功能性分析
分析:大多数字段是一样的,那么就是实际的表字段,唯一的区别就是各系统内的名字可能不一样,少数每个系统独有的字段,可以归为动态字段。
总结:
公共字段(翻译表头:
@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 组件来进行导入,实现公共列和动态列组合类型的导入,以及如何存储的功能,主要利用反射和字典分别来维护公共列和动态列的表头和字段的对应关系,利用此关系对数据进行解析。
边栏推荐
- MySQL相关介绍
- 设置海思芯片MMZ内存、OS内存详解
- Difference and performance comparison between HAL and LL library of STM32
- 2021年数据泄露成本报告解读
- How to analyze the weekly activity rate?
- I am doing open source in Didi
- C专家编程 第3章 分析C语言的声明 3.6 typedef int x[10]和#define x int[10]的区别
- 使用uniapp 封装一个request 请求
- leetcode:202. 快乐数
- 【带你了解SDN和网络虚拟化】
猜你喜欢

vector类

使用uniapp 封装一个request 请求

protobuf 反射使用总结

Detailed ReentrantLock

QT QT 】 【 to have developed a good program for packaging into a dynamic library

面了个腾讯35k出来的,他让我见识到什么叫精通MySQL调优

我在滴滴做开源

How much do you know about the intelligent operation and maintenance service of data warehouse based on DMS?

甲方不让用开源【监控软件】?大不了我自己写一个

滑环安装注意事项
随机推荐
[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 01
高薪程序员&面试题精讲系列132之微服务之间如何进行通信?服务熔断是怎么回事?你熟悉Hystrix吗?
测试测试测试
Common distributed theories (CAP, BASE) and consensus protocols (Gosssip, Raft)
附录A 程序员工作面试的秘密
WordPress 5.2.3 更新,升级出现请求超时的解决方法
蒋松廷 荣获第六季完美童模全球总决赛 全球总冠军
Windows 事件查看器记录到 MYSQL
C专家编程 第3章 分析C语言的声明 3.1 只有编译器才会喜欢的语法
Leetcode76. 最小覆盖子串
使用 PowerShell 将 Windows 转发事件导入 SQL Server
一文看懂推荐系统:召回02:Swing 模型,和itemCF很相似,区别在于计算相似度的方法不一样
C语言04、操作符
C专家编程 第3章 分析C语言的声明 3.6 typedef int x[10]和#define x int[10]的区别
如何设计大电流九线导电滑环
面试不再被吊打!这才是Redis分布式锁的七种方案的正确打开方式
Small Tools (4) integrated Seata1.5.2 distributed transactions
使用.NET简单实现一个Redis的高性能克隆版(一)
MATLAB | 一种简易的随机曼陀罗图形生成函数
如何使用MATLAB绘制极坐标堆叠柱状图