Spring Boot 通用对象列表比较和去重
2021/8/19 23:37:20
本文主要是介绍Spring Boot 通用对象列表比较和去重,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
1、前言
在Excel批量导入数据时,常常需要与数据库中已存在数据的比较,并且需要考虑导入数据重复的可能性。
导入的行数据,一般有一个实体类(对象)与之对应,往往这个实体类在数据库中的字段要比导入数据更多,如主键ID字段,这个ID字段一般不会出现在导入行数据中,此时导入的对象使用其它的唯一键来识别,如导入某个单位的用户数据,用用户名或手机号来唯一识别用户,而不会有用户ID字段(此时新用户的用户ID还有待系统来分配)。
批量导入数据,往往需要与已存在对象数据进行比较,新的对象使用insert操作,已有对象使用update操作。但如果逐条数据,都先查询数据库,再决定是insert,还是update,是非常低效的,代码也会很臃肿。使用类似Mysql的ON DUPLICATE KEY UPDATE ,是一种解决方案,但在使用全局ID时,可能存在ID主键与其它唯一主键冲突的情况,导致update失败。
因此,稳妥的作法,是比较导入对象集与存在对象集,比较的结果产生了4个集合:新增数据集合、属性值完全相同的对象集合、对象键值相同但属性有变化的对象集合、剩余对象集合。
一般批量导入数据,会有一个限定,如导入用户数据,限定导入某一个单位的,这样比较的数据集就不会太大。
因此,对象列表的比较,是批量数据导入的基础性功能。另外,导入数据,首先要消除数据中重复的对象,这些重复的对象数据是异常数据。
2、解决方案
对象列表比较,应支持通用实体类对象,这样可实现代码的复用,消除低效重复的针对具体实体类的代码。因此需要支持泛型,且使用反射机制。
另外,对象比较,是一组属性字段值比较,包括键值与普通属性值,键值用于识别对象。
代码如下:
package com.abc.example.common.utils; import java.lang.reflect.Field; import java.util.List; import java.util.Map; import lombok.extern.slf4j.Slf4j; /** * @className : ObjectListUtil * @description : 对象列表工具类 * @summary : * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2021/08/15 1.0.0 sheng.zheng 初版 * */ @Slf4j public class ObjectListUtil { /** * * @methodName : compareTwoList * @description : 比较两个对象列表,新列表与旧列表对比,根据比较的属性字段字典,比较结果 * 1、新的对象;2、键值相同,属性不同,需要修改的对象;3、属性完全相同 * 这样有新增对象列表,修改对象列表,相同对象列表,剩余对象列表 * @param <T> : 泛型类型T * @param fieldMap : 比较字段字典,key为字段名称,value为是否键值,1表示键值字段,0为普通字段。 * @param newList : 新列表,要求键值无重复 * @param oldList : 旧列表,要求键值无重复,在方法中将被改变 * @param addList : 新增对象列表 * @param sameList : 相同对象列表 * @param updateList: 修改对象列表 * @param remainderList: 剩余对象列表 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2021/08/15 1.0.0 sheng.zheng 初版 * */ public static <T> void compareTwoList(Map<String,Integer> fieldMap, List<T> newList,List<T> oldList, List<T> addList, List<T> sameList, List<T> updateList, List<T> remainderList) { // 遍历新列表 for(int i = 0; i < newList.size(); i++) { T newItem = newList.get(i); // 标记对象是否匹配 boolean found = false; // 遍历旧列表,倒序 for (int j = oldList.size() - 1; j >= 0; j--) { T oldItem = oldList.get(j); // 比较两个对象 int compare = compareTwoItem(fieldMap,newItem,oldItem); if (compare == 1) { // 如果两个对象相同,加入相同列表中 sameList.add(newItem); // 从列表中移除 oldList.remove(j); found = true; // 结束本轮遍历 break; }else if(compare == 2) { // pass }else if(compare == 3) { // 匹配对象,但属性不同,加入修改表中 updateList.add(newItem); // 从列表中移除 oldList.remove(j); found = true; // 结束本轮遍历 break; }else { // 发生异常 return; } } if (found == false) { // 如果本轮遍历,未找到匹配项,加入新增列表中 addList.add(newItem); } } // oldList中剩余的项,加入剩余列表中 for(int i = 0; i < oldList.size(); i++) { T oldItem = oldList.get(i); remainderList.add(oldItem); } } /** * * @methodName : compareTwoItem * @description : 比较两个相同类型的对象 * @param <T> : 泛型类型T * @param fieldMap : 比较字段字典,key为字段名称,value为是否键值,1表示键值字段。 * @param newItem : 新的对象 * @param oldItem : 旧的对象 * @return : 返回值定义如下: * 0 : 数据处理异常 * 1 : 对象完全相同(比较字段的字段值都相同) * 2 : 对象不同(键值字段的字段值存在不相同) * 3 : 对象相同,属性不同(键值字段的字段值相同,但属性值不同) * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2021/08/15 1.0.0 sheng.zheng 初版 * */ @SuppressWarnings("unchecked") public static <T> int compareTwoItem(Map<String,Integer> fieldMap,T newItem, T oldItem) { int retCode = 1; try { for (Map.Entry<String,Integer> entry : fieldMap.entrySet()) { // 取得字段名称 String fieldName = entry.getKey(); Integer keyFlag = entry.getValue(); // 取得新对象的当前字段值 Class<T> newClazz = (Class<T>) newItem.getClass(); Field newField = newClazz.getDeclaredField(fieldName); newField.setAccessible(true); Object newValue = newField.get(newItem); // 取得旧对象的当前字段值 Class<T> oldClazz = (Class<T>) oldItem.getClass(); Field oldField = oldClazz.getDeclaredField(fieldName); oldField.setAccessible(true); Object oldValue = oldField.get(oldItem); // 比较两个属性字段的值 if (!newValue.equals(oldValue)) { // 如果字段值不相等 if (keyFlag == 1) { // 如果为键值字段,表示两个对象 return 2; }else { // 非键值字段,表示有属性发生改变 retCode = 3; } } } }catch (NoSuchFieldException e) { e.printStackTrace(); log.error(e.getMessage()); return 0; }catch (IllegalAccessException e) { e.printStackTrace(); log.error(e.getMessage()); return 0; } return retCode; } /** * * @methodName : removeDuplicate * @description : 对象列表去重 * @param <T> : 泛型类型T * @param fieldMap : 比较字段字典,key为字段名称,value为是否键值,1表示键值字段。 * @param inputList : 对象列表,将被去重 * @param dupList : 重复的多余对象列表,将被去重 * @history : * ------------------------------------------------------------------------------ * date version modifier remarks * ------------------------------------------------------------------------------ * 2021/08/15 1.0.0 sheng.zheng 初版 * */ public static <T> void removeDuplicate(Map<String,Integer> fieldMap,List<T> inputList,List<T> dupList){ // 开始比较下标 int pos = 0; while (true) { if (inputList.size() -1 < pos) { break; } // 标记对象是否重复 boolean found = false; // 被比较对象 T compItem = inputList.get(pos); // 遍历列表 for (int i = pos + 1; i < inputList.size(); i++) { // 比较对象 T newItem = inputList.get(i); int compare = compareTwoItem(fieldMap,newItem,compItem); if (compare == 1 || compare == 3) { // 键值相同,为重复对象 found = true; // 结束本次比较 break; } } if (found == true) { // 对象重复 dupList.add(compItem); // 移除重复项 inputList.remove(pos); // 注意,移除后,当前位置不变 }else { // 不重复,处理下一个 pos ++; } } } }
3、调用方法
调用方法如下:
// 去重处理 // 字段字典 Map<String,Integer> fieldMap = new HashMap<String,Integer>(); // 手机号和用户名作为对象识别属性 fieldMap.put("phoneNumber", 1); fieldMap.put("userName", 1); List<UserInfo> dupList = new ArrayList<UserInfo>(); ObjectListUtil.removeDuplicate(fieldMap,importUserList,dupList); // dupList可以作为导入错误数据输出用 // 查询数据库中存在的数据,orgId为导入附加参数,表示单位ID List<UserInfo> dbUserList = userDao.selectItemsByOrgId(orgId); // 对比新旧数据列表 List<UserInfo> addList = new ArrayList<UserInfo>(); List<UserInfo> updateList = new ArrayList<UserInfo>(); List<UserInfo> sameList = new ArrayList<UserInfo>(); List<UserInfo> remainderList = new ArrayList<UserInfo>(); // 对象的其它属性字段,非键值 fieldMap.put("age", 0); fieldMap.put("address", 0); fieldMap.put("height", 0); ObjectListUtil.compareTwoList(fieldMap, importUserList, dbUserList, addList, sameList, updateList, remainderList); // 对addList执行insert操作,可以批量insert // 对updateList执行update操作,逐个update
这篇关于Spring Boot 通用对象列表比较和去重的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-22项目:远程温湿度检测系统
- 2024-12-21《鸿蒙HarmonyOS应用开发从入门到精通(第2版)》简介
- 2024-12-21后台管理系统开发教程:新手入门全指南
- 2024-12-21后台开发教程:新手入门及实战指南
- 2024-12-21后台综合解决方案教程:新手入门指南
- 2024-12-21接口模块封装教程:新手必备指南
- 2024-12-21请求动作封装教程:新手必看指南
- 2024-12-21RBAC的权限教程:从入门到实践
- 2024-12-21登录鉴权实战:新手入门教程
- 2024-12-21动态权限实战入门指南