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 = https://www.cnblogs.com/alabo1999/p/newField.get(newItem);
// 取得舊物件的當前字段值
Class oldClazz = (Class) 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
* @param fieldMap : 比較欄位字典,key為欄位名稱,value為是否鍵值,1表示鍵值欄位,
* @param inputList : 物件串列,將被去重
* @param dupList : 重復的多余物件串列,將被去重
* @history :
* ------------------------------------------------------------------------------
* date version modifier remarks
* ------------------------------------------------------------------------------
* 2021/08/15 1.0.0 sheng.zheng 初版
*
*/
public static void removeDuplicate(Map fieldMap,List inputList,List 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
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/294838.html
標籤:Java
上一篇:微服務架構如何避免大規模故障?
下一篇:requests模塊的使用
