Java實(shí)現(xiàn)對比兩個實(shí)體類字段變化的示例詳解
一、前言
我們在工作中,可能會在日志中記錄數(shù)據(jù)的變化情況或者在公共處理的數(shù)據(jù)增加一個日志頁面,記錄每次修改的變化。我們可以根據(jù)CompareUtils工具類比較數(shù)據(jù)前后發(fā)生了怎樣的變化,這樣我們就可以知道數(shù)據(jù)做了哪些改變。
二、條件限制
在寫這個通用方法時,我們應(yīng)該考慮到以下幾點(diǎn):
(1)可以接收任何對象的比較,但比較的對象應(yīng)該是同個對象;
(2)可以給字段進(jìn)行一個備注,因?yàn)槲覀兛吹降淖罱K內(nèi)容,應(yīng)該是一個中文名稱;
(3)一個對象中,可以注解來獲取某些字段進(jìn)行比較,沒有注解的不需要進(jìn)行比較;
(4)一個對象中,可以使用其他字段進(jìn)行比較,輸出某些字段,如對字段userName進(jìn)行注解,使用userId(用戶id)進(jìn)行比較,輸出userName(用戶名稱);
(5)一個對象中,可以對字段進(jìn)行格式化輸出,如金額、日期時間等。
三、代碼
CompareAnon(比較接口)(自定義注解)
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 對比注解 */ @Target(value = {ElementType.TYPE, ElementType.FIELD}) // 允許被修飾的注解作用在類、接口和枚舉上 // 允許作用在屬性字段上 @Retention(RetentionPolicy.RUNTIME) // 注解在運(yùn)行時保留,可通過反射獲取 public @interface CompareAnon { /** * 字段中文名稱 */ String name(); /** * 前綴字段(進(jìn)行拼接,即:prefixField字段值 + name) */ String prefixField() default ""; /** * 比較字段(不用原字段進(jìn)行比較) */ String compareField() default ""; /** * 新增、刪除時,是否用該值當(dāng)做“字段值” * 如: * 新增 -> 字段值 * 字段值 -> 刪除 */ boolean asContent() default false; /** * 格式 */ String pattern() default ""; }
ComparedResult(對比結(jié)果)
import java.io.Serializable; /** * 對比結(jié)果 */ public class ComparedResult implements Serializable { /** * 對比字段 */ private String field; /** * 對比字段名稱(說明) */ private String fieldName; /** * 原來的值 */ private Object oldValue; /** * 修改后的值 */ private Object newValue; /** * 原來的值(字符串) */ private String oldContent; /** * 修改后的值(字符串) */ private String newContent; /** * 格式 */ private String pattern; /** * 備注 */ private String remark; public String getField() { return field; } public void setField(String field) { this.field = field; } public String getFieldName() { return fieldName; } public void setFieldName(String fieldName) { this.fieldName = fieldName; } public Object getOldValue() { return oldValue; } public void setOldValue(Object oldValue) { this.oldValue = oldValue; } public Object getNewValue() { return newValue; } public void setNewValue(Object newValue) { this.newValue = newValue; } public String getOldContent() { return oldContent; } public void setOldContent(String oldContent) { this.oldContent = oldContent; } public String getNewContent() { return newContent; } public void setNewContent(String newContent) { this.newContent = newContent; } public String getPattern() { return pattern; } public void setPattern(String pattern) { this.pattern = pattern; } public String getRemark() { return remark; } public void setRemark(String remark) { this.remark = remark; } @Override public String toString() { return "ComparedResult{" + "field='" + field + '\'' + ", fieldName='" + fieldName + '\'' + ", oldValue=" + oldValue + ", newValue=" + newValue + ", oldContent='" + oldContent + '\'' + ", newContent='" + newContent + '\'' + ", pattern='" + pattern + '\'' + ", remark='" + remark + '\'' + '}'; } }
訂單DTO(測試類)
import java.io.Serializable; import java.math.BigDecimal; import java.util.Date; /** * 訂單DTO */ @CompareAnon(name = "訂單") public class OrderDTO implements Serializable { @CompareAnon(name = "訂單id") private String id; @CompareAnon(name = "訂單編號", asContent = true) private String orderCode; private String supplyId; @CompareAnon(name = "供應(yīng)商名稱", compareField = "supplyId") private String supplyName; @CompareAnon(name = "訂單金額(元)", pattern = "#,##0.0000") private BigDecimal orderAmount; @CompareAnon(name = "下單日期", pattern = "yyyy-MM-dd HH:mm:ss") private Date orderDate; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getOrderCode() { return orderCode; } public void setOrderCode(String orderCode) { this.orderCode = orderCode; } public String getSupplyId() { return supplyId; } public void setSupplyId(String supplyId) { this.supplyId = supplyId; } public String getSupplyName() { return supplyName; } public void setSupplyName(String supplyName) { this.supplyName = supplyName; } public BigDecimal getOrderAmount() { return orderAmount; } public void setOrderAmount(BigDecimal orderAmount) { this.orderAmount = orderAmount; } public Date getOrderDate() { return orderDate; } public void setOrderDate(Date orderDate) { this.orderDate = orderDate; } }
CompareUtils(比較工具類)
import org.apache.commons.lang.StringUtils; import java.beans.PropertyDescriptor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.math.BigDecimal; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Objects; /** * 比較工具類 */ public class CompareUtils { /** * 比較不同值,是否相等 * * @param o1 值1 * @param o2 值2 * @return 比較結(jié)果,true相等,false不相等 */ public static boolean compareValues(Object o1, Object o2) { if (o1 == null && o2 == null) { return true; } if (o1 != null && o2 == null) { return false; } if (o1 == null && o2 != null) { return false; } // BigDecimal比較用compareTo if (o1 instanceof BigDecimal && o2 instanceof BigDecimal) { BigDecimal decimal1 = new BigDecimal(String.valueOf(o1)); BigDecimal decimal2 = new BigDecimal(String.valueOf(o2)); // 0相等 return decimal1.compareTo(decimal2) == 0; } if (o1 instanceof Timestamp) { o1 = new Date(((Timestamp) o1).getTime()); } if (o2 instanceof Timestamp) { o2 = new Date(((Timestamp) o2).getTime()); } return Objects.equals(o1, o2); } /** * 對比兩個對象修改值 * @param oldObject 舊對象 * @param newObject 新對象 * @return 對比結(jié)果列表 */ public static List<ComparedResult> compareTwoObject(Object oldObject, Object newObject) { // 為null時,不比較 if (null == oldObject || null == newObject) { return Collections.emptyList(); } // 不是同一個class if (oldObject.getClass() != newObject.getClass()) { throw new RuntimeException("不是同一個class!"); } List<ComparedResult> comparedResultList = new ArrayList<>(); Class<?> clazz = oldObject.getClass(); // 獲取所有字段 Field[] Fields = clazz.getDeclaredFields(); for (Field field : Fields) { try { // 獲取注解 CompareAnon compareAnon = field.getAnnotation(CompareAnon.class); // 沒有注解,說明不對比字段,跳過 if (null == compareAnon) { continue; } String fieldKey = field.getName(); // 字段名 String filedName = compareAnon.name(); // 字段中文名稱 String pattern = compareAnon.pattern(); // 格式 // 前綴字段,存在(進(jìn)行拼接,即:prefixField字段值 + name) String prefixField = compareAnon.prefixField(); if (StringUtils.isNotBlank(prefixField)) { filedName = getFieldValue(prefixField, oldObject) + filedName; } // 比較字段名 String compareField = field.getName(); if (StringUtils.isNotBlank(compareAnon.compareField())) { compareField = compareAnon.compareField(); } // 獲取比較的屬性值 // 在oldObject上調(diào)用get方法等同于獲得oldObject的屬性值 Object oldCompareValue = getFieldValue(compareField, oldObject); // 在newObject上調(diào)用get方法等同于獲得newObject的屬性值 Object newCompareValue = getFieldValue(compareField, newObject); // 在oldObject上調(diào)用get方法等同于獲得oldObject的屬性值 Object oldValue = getFieldValue(fieldKey, oldObject); // 在newObject上調(diào)用get方法等同于獲得newObject的屬性值 Object newValue = getFieldValue(fieldKey, newObject); // 原來的值(字符串) String oldContent = CastUtils.getFormatValue(oldValue, pattern); // 修改后的值(字符串) String newContent = CastUtils.getFormatValue(newValue, pattern); // 對比兩個值是否一致 if (!compareValues(oldCompareValue, newCompareValue)) { // 不一致 ComparedResult result = new ComparedResult(); result.setField(fieldKey); result.setFieldName(filedName); result.setOldValue(oldValue); result.setNewValue(newValue); result.setOldContent(oldContent); result.setNewContent(newContent); result.setPattern(pattern); result.setRemark(oldContent + " -> " + newContent); comparedResultList.add(result); } } catch (Exception e) { e.printStackTrace(); } } return comparedResultList; } /** * 對比兩個對象修改值(檢查是否為null) * @param oldObject 舊對象 * @param newObject 新對象 * @return 對比結(jié)果列表 */ public static List<ComparedResult> compareTwoObjectCheckNull(Object oldObject, Object newObject) { // 都為null時,不比較 if (null == oldObject && null == newObject) { return Collections.emptyList(); } // 新增 if (null == oldObject && null != newObject) { // 返回新增對比結(jié)果 return addComparedResult(newObject); } // 刪除 if (null != oldObject && null == newObject) { // 返回刪除對比結(jié)果 return deleteComparedResult(oldObject); } // 都不為null時,對比兩個對象修改值 return compareTwoObject(oldObject, newObject); } /** * 新增對比結(jié)果 * @param obj 對象 * @return 對比結(jié)果 */ public static List<ComparedResult> addComparedResult(Object obj) { List<ComparedResult> comparedResultList = new ArrayList<>(); Class<?> clazz = obj.getClass(); String filedName = ""; // 字段中文名稱 String content = ""; // 內(nèi)容 // 獲取類注解 CompareAnon anon = clazz.getAnnotation(CompareAnon.class); if (null != anon) { filedName = anon.name(); } // 獲取所有字段 Field[] Fields = clazz.getDeclaredFields(); for (Field field : Fields) { try { // 獲取注解 CompareAnon compareAnon = field.getAnnotation(CompareAnon.class); // 沒有注解,說明不對比字段,跳過 if (null == compareAnon) { continue; } String fieldKey = field.getName(); // 字段名 boolean asContent = compareAnon.asContent(); // 是否用該值當(dāng)做“字段值” if (asContent) { Object value = getFieldValue(fieldKey, obj); // 字段值 content = CastUtils.getFormatValue(value, null); } } catch (Exception e) { e.printStackTrace(); } } // 新增結(jié)果 ComparedResult result = new ComparedResult(); result.setFieldName(filedName); result.setOldContent("新增"); result.setNewContent(content); result.setRemark("新增"); comparedResultList.add(result); return comparedResultList; } /** * 刪除對比結(jié)果 * @param obj 對象 * @return 對比結(jié)果 */ public static List<ComparedResult> deleteComparedResult(Object obj) { List<ComparedResult> comparedResultList = new ArrayList<>(); Class<?> clazz = obj.getClass(); String filedName = ""; // 字段中文名稱 String content = ""; // 內(nèi)容 // 獲取class注解 CompareAnon anon = clazz.getAnnotation(CompareAnon.class); if (null != anon) { filedName = anon.name(); } // 獲取所有字段 Field[] Fields = clazz.getDeclaredFields(); for (Field field : Fields) { try { // 獲取注解 CompareAnon compareAnon = field.getAnnotation(CompareAnon.class); // 沒有注解,說明不對比字段,跳過 if (null == compareAnon) { continue; } String fieldKey = field.getName(); // 字段名 boolean asContent = compareAnon.asContent(); // 是否用該值當(dāng)做“字段值” if (asContent) { Object value = getFieldValue(fieldKey, obj); // 字段值 content = CastUtils.getFormatValue(value, null); } } catch (Exception e) { e.printStackTrace(); } } // 刪除結(jié)果 ComparedResult result = new ComparedResult(); result.setFieldName(filedName); result.setOldContent(content); result.setNewContent("刪除"); result.setRemark("刪除"); comparedResultList.add(result); return comparedResultList; } /** * 獲取字段值 * @param fieldKey 字段名稱 * @param obj 對象 * @return 字段值 * @throws Exception 異常 */ public static Object getFieldValue(String fieldKey, Object obj) throws Exception { PropertyDescriptor pd = new PropertyDescriptor(fieldKey, obj.getClass()); Method getMethod = pd.getReadMethod(); // 在obj上調(diào)用get方法等同于獲得obj的屬性值 return getMethod.invoke(obj); } public static void main(String[] args) { System.out.println(compareValues(null, null)); System.out.println(compareValues(2, null)); System.out.println(compareValues(null, 2)); System.out.println(compareValues(2, 2)); System.out.println(compareValues("2", "2")); System.out.println(compareValues(2, "2")); System.out.println("------------------------BigDecimal---------------------------"); System.out.println(compareValues(new BigDecimal("2"), "2")); System.out.println(compareValues(new BigDecimal("2"), new BigDecimal("2.0000"))); System.out.println(compareValues(new BigDecimal("2.00"), new BigDecimal("2.0000"))); System.out.println(compareValues(new BigDecimal("2.0000"), new BigDecimal("2.0000"))); System.out.println(compareValues(new BigDecimal("2.0000"), new BigDecimal("3.0000"))); System.out.println("------------------------comparedResultList---------------------------"); OrderDTO oldDTO = new OrderDTO(); oldDTO.setId("1"); oldDTO.setOrderCode("訂單1"); oldDTO.setSupplyId("1"); oldDTO.setSupplyName("供應(yīng)商名稱1"); oldDTO.setOrderAmount(new BigDecimal("111111")); oldDTO.setOrderDate(new Date()); OrderDTO newDTO = new OrderDTO(); newDTO.setId("2"); newDTO.setOrderCode("訂單2"); newDTO.setSupplyId("2"); newDTO.setSupplyName("供應(yīng)商名稱2"); newDTO.setOrderAmount(new BigDecimal("222222")); newDTO.setOrderDate(DateUtil.getBelowDay(new Date())); List<ComparedResult> comparedResultList = compareTwoObject(oldDTO, newDTO); for (ComparedResult comparedResult : comparedResultList) { System.out.println(comparedResult.toString()); } System.out.println("------------------------comparedResultList2---------------------------"); OrderDTO orderDTO = new OrderDTO(); orderDTO.setId("3"); orderDTO.setOrderCode("訂單3"); orderDTO.setSupplyId("3"); orderDTO.setSupplyName("供應(yīng)商名稱3"); orderDTO.setOrderAmount(new BigDecimal("333333")); orderDTO.setOrderDate(DateUtil.getBelowDay(new Date())); List<ComparedResult> comparedResultList2 = compareTwoObjectCheckNull(null, orderDTO); for (ComparedResult comparedResult : comparedResultList2) { System.out.println(comparedResult.toString()); } System.out.println("------------------------comparedResultList3---------------------------"); List<ComparedResult> comparedResultList3 = compareTwoObjectCheckNull(orderDTO, null); for (ComparedResult comparedResult : comparedResultList3) { System.out.println(comparedResult.toString()); } } }
四、main方法,輸出結(jié)果
true
false
false
true
true
false
------------------------BigDecimal---------------------------
false
true
true
true
false
------------------------comparedResultList---------------------------
ComparedResult{field='id', fieldName='訂單id', oldValue=1, newValue=2, oldContent='1', newContent='2', pattern='', remark='1 -> 2'}
ComparedResult{field='orderCode', fieldName='訂單編號', oldValue=訂單1, newValue=訂單2, oldContent='訂單1', newContent='訂單2', pattern='', remark='訂單1 -> 訂單2'}
ComparedResult{field='supplyName', fieldName='供應(yīng)商名稱', oldValue=供應(yīng)商名稱1, newValue=供應(yīng)商名稱2, oldContent='供應(yīng)商名稱1', newContent='供應(yīng)商名稱2', pattern='', remark='供應(yīng)商名稱1 -> 供應(yīng)商名稱2'}
ComparedResult{field='orderAmount', fieldName='訂單金額(元)', oldValue=111111, newValue=222222, oldContent='111,111.0000', newContent='222,222.0000', pattern='#,##0.0000', remark='111,111.0000 -> 222,222.0000'}
ComparedResult{field='orderDate', fieldName='下單日期', oldValue=Wed Mar 20 11:51:22 CST 2024, newValue=Thu Mar 21 11:51:22 CST 2024, oldContent='2024-03-20 11:51:22', newContent='2024-03-21 11:51:22', pattern='yyyy-MM-dd HH:mm:ss', remark='2024-03-20 11:51:22 -> 2024-03-21 11:51:22'}
------------------------comparedResultList2---------------------------
ComparedResult{field='null', fieldName='訂單', oldValue=null, newValue=null, oldContent='新增', newContent='訂單3', pattern='null', remark='新增'}
------------------------comparedResultList3---------------------------
ComparedResult{field='null', fieldName='訂單', oldValue=null, newValue=null, oldContent='訂單3', newContent='刪除', pattern='null', remark='刪除'}
以上就是Java實(shí)現(xiàn)對比兩個實(shí)體類字段變化的示例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java對比實(shí)體類字段變化的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringSecurity實(shí)現(xiàn)自定義登錄方式
本文介紹自定義登錄流程,包括自定義AuthenticationToken、AuthenticationFilter、AuthenticationProvider以及SecurityConfig配置類,詳細(xì)解析了認(rèn)證流程的實(shí)現(xiàn),為開發(fā)人員提供了具體的實(shí)施指導(dǎo)和參考2024-09-09IDEA新建bootstrap.yml文件不顯示葉子圖標(biāo)的問題
這篇文章主要介紹了IDEA新建bootstrap.yml文件不顯示葉子圖標(biāo)的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07java日期操作工具類(獲取指定日期、日期轉(zhuǎn)換、相隔天數(shù))
這篇文章主要為大家詳細(xì)介紹了java日期操作工具類,包括獲取指定日期、日期轉(zhuǎn)換、相隔天數(shù)等操作,感興趣的小伙伴們可以參考一下2016-06-06mybatis+lombok出現(xiàn)java.lang.IndexOutOfBoundsException錯誤及解決
在使用MyBatis和Lombok時,如果遇到j(luò)ava.lang.IndexOutOfBoundsException問題,是因?yàn)镸yBatis在嘗試將查詢結(jié)果封裝成Java對象時,找不到構(gòu)造函數(shù)中對應(yīng)的字段,這通常是由于Lombok的@Builder注解生成了全參構(gòu)造函數(shù)2025-02-02