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)文章
分析講解SpringMVC注解配置如何實(shí)現(xiàn)
這篇文章主要介紹了本文要介紹用注解方式代替web.xml與SpringMVC的配置文件,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-05-05
Java注解@Transactional事務(wù)類內(nèi)調(diào)用不生效問題及解決辦法
這篇文章主要介紹了Java注解@Transactional事務(wù)類內(nèi)調(diào)用不生效問題及解決辦法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05
細(xì)談java同步之JMM(Java Memory Model)
Java內(nèi)存模型是在硬件內(nèi)存模型上的更高層的抽象,它屏蔽了各種硬件和操作系統(tǒng)訪問的差異性,保證了Java程序在各種平臺下對內(nèi)存的訪問都能達(dá)到一致的效果。下面我們來一起學(xué)習(xí)下JMM2019-05-05

