SoFunction
Updated on 2025-03-06

A detailed explanation of Java implementation comparing the changes of two entity class fields

1. Preface

At work, we may record data changes in the log or add a log page to the publicly processed data to record changes in each modification. We can compare the changes before and after the data according to the CompareUtils tool class, so that we can know what changes the data has made.

2. Conditional restrictions

When writing this general method, we should consider the following points:

(1) You can receive comparisons of any object, but the comparison object should be the same object;

(2) You can make a note to the field, because the final content we see should be a Chinese name;

(3) In an object, you can get some fields for comparison, and those without annotations do not need to compare;

(4) In an object, other fields can be used for comparison and output certain fields, such as annotating the field userName, using userId (user ID) to compare, and output userName (user name);

(5) In an object, fields can be formatted and output, such as amount, date and time, etc.

3. Code

CompareAnon (Compare Interface) (Custom Annotation)

import ;
import ;
import ;
import ;
 
/**
  * Comparative Notes
  */
@Target(value = {, }) // Allow modified annotations to be used on classes, interfaces and enums // Allow to be used on attribute fields@Retention() // Annotations are retained at runtime and can be obtained through reflectionpublic @interface CompareAnon {
 
    /**
      * Field Chinese name
      */
    String name();
 
    /**
      * Prefix field (split, i.e.: prefixField field value + name)
      */
    String prefixField() default "";
 
    /**
      * Compare fields (without the original field for comparison)
      */
    String compareField() default "";
 
    /**
      * When adding or deleting, whether to use this value as the "field value"
      * like:
      * Added -> Field value
      * Field value -> Delete
      */
    boolean asContent() default false;
 
    /**
      * Format
      */
    String pattern() default "";
 
}

ComparedResult

import ;
 
/**
  * Comparison results
  */
public class ComparedResult implements Serializable {
 
    /**
      * Comparison fields
      */
    private String field;
 
    /**
      * Comparison field name (description)
      */
    private String fieldName;
 
    /**
      * Original value
      */
    private Object oldValue;
 
    /**
      * Modified value
      */
    private Object newValue;
 
    /**
      * Original value (string)
      */
    private String oldContent;
 
    /**
      * Modified value (string)
      */
    private String newContent;
 
    /**
      * Format
      */
    private String pattern;
 
    /**
      * Remark
      */
    private String remark;
 
    public String getField() {
        return field;
    }
 
    public void setField(String field) {
         = field;
    }
 
    public String getFieldName() {
        return fieldName;
    }
 
    public void setFieldName(String fieldName) {
         = fieldName;
    }
 
    public Object getOldValue() {
        return oldValue;
    }
 
    public void setOldValue(Object oldValue) {
         = oldValue;
    }
 
    public Object getNewValue() {
        return newValue;
    }
 
    public void setNewValue(Object newValue) {
         = newValue;
    }
 
    public String getOldContent() {
        return oldContent;
    }
 
    public void setOldContent(String oldContent) {
         = oldContent;
    }
 
    public String getNewContent() {
        return newContent;
    }
 
    public void setNewContent(String newContent) {
         = newContent;
    }
 
    public String getPattern() {
        return pattern;
    }
 
    public void setPattern(String pattern) {
         = pattern;
    }
 
    public String getRemark() {
        return remark;
    }
 
    public void setRemark(String remark) {
         = remark;
    }
 
    @Override
    public String toString() {
        return "ComparedResult{" +
                "field='" + field + '\'' +
                ", fieldName='" + fieldName + '\'' +
                ", oldValue=" + oldValue +
                ", newValue=" + newValue +
                ", oldContent='" + oldContent + '\'' +
                ", newContent='" + newContent + '\'' +
                ", pattern='" + pattern + '\'' +
                ", remark='" + remark + '\'' +
                '}';
    }
}

Order DTO (test class)

import ;
import ;
import ;
 
/**
  * Order DTO
  */
@CompareAnon(name = "Order")
public class OrderDTO implements Serializable {
 
    @CompareAnon(name = "Order id")
    private String id;
 
    @CompareAnon(name = "Order number", asContent = true)
    private String orderCode;
 
    private String supplyId;
 
    @CompareAnon(name = "Supplier Name", compareField = "supplyId")
    private String supplyName;
 
    @CompareAnon(name = "Order Amount (yuan)", pattern = "#,##0.0000")
    private BigDecimal orderAmount;
 
    @CompareAnon(name = "Order Date", pattern = "yyyy-MM-dd HH:mm:ss")
    private Date orderDate;
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
         = id;
    }
 
    public String getOrderCode() {
        return orderCode;
    }
 
    public void setOrderCode(String orderCode) {
         = orderCode;
    }
 
    public String getSupplyId() {
        return supplyId;
    }
 
    public void setSupplyId(String supplyId) {
         = supplyId;
    }
 
    public String getSupplyName() {
        return supplyName;
    }
 
    public void setSupplyName(String supplyName) {
         = supplyName;
    }
 
    public BigDecimal getOrderAmount() {
        return orderAmount;
    }
 
    public void setOrderAmount(BigDecimal orderAmount) {
         = orderAmount;
    }
 
    public Date getOrderDate() {
        return orderDate;
    }
 
    public void setOrderDate(Date orderDate) {
         = orderDate;
    }
 
}

CompareUtils (Compare Tool Class)

import ;
 
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
import ;
 
/**
  * Comparison tool class
  */
public class CompareUtils {
 
    /**
      * Compare different values, whether they are equal
      *
      * @param o1 value 1
      * @param o2 value 2
      * @return Comparison results, true is equal, false is not equal
      */
    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 uses compareTo        if (o1 instanceof BigDecimal && o2 instanceof BigDecimal) {
            BigDecimal decimal1 = new BigDecimal((o1));
            BigDecimal decimal2 = new BigDecimal((o2));
            // 0 equal            return (decimal2) == 0;
        }
 
        if (o1 instanceof Timestamp) {
            o1 = new Date(((Timestamp) o1).getTime());
        }
        if (o2 instanceof Timestamp) {
            o2 = new Date(((Timestamp) o2).getTime());
        }
 
        return (o1, o2);
    }
 
    /**
      * Compare the two objects to modify the value
      * @param oldObject OldObject
      * @param newObject
      * @return Comparison result list
      */
    public static List<ComparedResult> compareTwoObject(Object oldObject, Object newObject) {
        // When null, no comparison        if (null == oldObject || null == newObject) {
            return ();
        }
 
        // Not the same class        if (() != ()) {
            throw new RuntimeException("Not the same class!");
        }
 
        List<ComparedResult> comparedResultList = new ArrayList<>();
        Class<?> clazz = ();
 
        // Get all fields        Field[] Fields = ();
        for (Field field : Fields) {
            try {
 
                // Get annotation                CompareAnon compareAnon = ();
                // No annotation means no comparison fields, skip                if (null == compareAnon) {
                    continue;
                }
 
                String fieldKey = (); // Field name                String filedName = (); // Field Chinese name                String pattern = (); // Format 
                // Prefix field exists (sticking, that is: prefixField field value + name)                String prefixField = ();
                if ((prefixField)) {
                    filedName = getFieldValue(prefixField, oldObject) + filedName;
                }
 
                // Compare field names                String compareField = ();
                if ((())) {
                    compareField = ();
                }
 
                // Get the value of the comparison attribute                // Calling the get method on oldObject is equivalent to getting the property value of oldObject                Object oldCompareValue = getFieldValue(compareField, oldObject);
                // Calling the get method on newObject is equivalent to getting the property value of newObject                Object newCompareValue = getFieldValue(compareField, newObject);
 
                // Calling the get method on oldObject is equivalent to getting the property value of oldObject                Object oldValue = getFieldValue(fieldKey, oldObject);
                // Calling the get method on newObject is equivalent to getting the property value of newObject                Object newValue = getFieldValue(fieldKey, newObject);
 
                // Original value (string)                String oldContent = (oldValue, pattern);
                // Modified value (string)                String newContent = (newValue, pattern);
 
                // Compare whether the two values ​​are consistent                if (!compareValues(oldCompareValue, newCompareValue)) {
                    // Inconsistent                    ComparedResult result = new ComparedResult();
                    (fieldKey);
                    (filedName);
                    (oldValue);
                    (newValue);
                    (oldContent);
                    (newContent);
                    (pattern);
                    (oldContent + " -> " + newContent);
                    (result);
                }
 
            } catch (Exception e) {
                ();
            }
        }
 
        return comparedResultList;
    }
 
    /**
      * Compare the two objects to modify the value (check whether it is null)
      * @param oldObject OldObject
      * @param newObject
      * @return Comparison result list
      */
    public static List<ComparedResult> compareTwoObjectCheckNull(Object oldObject, Object newObject) {
        // When all are null, no comparison        if (null == oldObject && null == newObject) {
            return ();
        }
 
        // Added        if (null == oldObject && null != newObject) {
            // Return to new comparison results            return addComparedResult(newObject);
        }
 
        // delete        if (null != oldObject && null == newObject) {
            // Return to delete comparison results            return deleteComparedResult(oldObject);
        }
 
        // When neither is null, compare the two objects to modify the value        return compareTwoObject(oldObject, newObject);
    }
 
    /**
      * Added comparison results
      * @param obj object
      * @return Comparison results
      */
    public static List<ComparedResult> addComparedResult(Object obj) {
        List<ComparedResult> comparedResultList = new ArrayList<>();
        Class<?> clazz = ();
 
        String filedName = ""; // Field Chinese name        String content = ""; // content 
        // Get class annotations        CompareAnon anon = ();
        if (null != anon) {
            filedName = ();
        }
 
        // Get all fields        Field[] Fields = ();
        for (Field field : Fields) {
            try {
 
                // Get annotation                CompareAnon compareAnon = ();
                // No annotation means no comparison fields, skip                if (null == compareAnon) {
                    continue;
                }
 
                String fieldKey = (); // Field name                boolean asContent = (); // Whether to use this value as a "field value" 
                if (asContent) {
                    Object value = getFieldValue(fieldKey, obj); // Field value                    content = (value, null);
                }
 
            } catch (Exception e) {
                ();
            }
        }
 
        // Add new results        ComparedResult result = new ComparedResult();
        (filedName);
        ("New");
        (content);
        ("New");
        (result);
 
        return comparedResultList;
    }
 
    /**
      * Delete the comparison results
      * @param obj object
      * @return Comparison results
      */
    public static List<ComparedResult> deleteComparedResult(Object obj) {
        List<ComparedResult> comparedResultList = new ArrayList<>();
        Class<?> clazz = ();
 
        String filedName = ""; // Field Chinese name        String content = ""; // content 
        // Get class annotation        CompareAnon anon = ();
        if (null != anon) {
            filedName = ();
        }
 
        // Get all fields        Field[] Fields = ();
        for (Field field : Fields) {
            try {
 
                // Get annotation                CompareAnon compareAnon = ();
                // No annotation means no comparison fields, skip                if (null == compareAnon) {
                    continue;
                }
 
                String fieldKey = (); // Field name                boolean asContent = (); // Whether to use this value as a "field value" 
                if (asContent) {
                    Object value = getFieldValue(fieldKey, obj); // Field value                    content = (value, null);
                }
 
            } catch (Exception e) {
                ();
            }
        }
 
        // Delete the result        ComparedResult result = new ComparedResult();
        (filedName);
        (content);
        ("delete");
        ("delete");
        (result);
 
        return comparedResultList;
    }
 
    /**
      * Get field value
      * @param fieldKey field name
      * @param obj object
      * @return field value
      * @throws Exception
      */
    public static Object getFieldValue(String fieldKey, Object obj) throws Exception {
        PropertyDescriptor pd = new PropertyDescriptor(fieldKey, ());
        Method getMethod = ();
 
        // Calling the get method on obj is equivalent to getting the attribute value of obj        return (obj);
    }
 
    public static void main(String[] args) {
 
        (compareValues(null, null));
        (compareValues(2, null));
        (compareValues(null, 2));
        (compareValues(2, 2));
        (compareValues("2", "2"));
        (compareValues(2, "2"));
 
        ("------------------------BigDecimal---------------------------");
 
        (compareValues(new BigDecimal("2"), "2"));
        (compareValues(new BigDecimal("2"), new BigDecimal("2.0000")));
        (compareValues(new BigDecimal("2.00"), new BigDecimal("2.0000")));
        (compareValues(new BigDecimal("2.0000"), new BigDecimal("2.0000")));
        (compareValues(new BigDecimal("2.0000"), new BigDecimal("3.0000")));
 
        ("------------------------comparedResultList---------------------------");
 
        OrderDTO oldDTO = new OrderDTO();
        ("1");
        ("Order 1");
        ("1");
        ("Supplier Name 1");
        (new BigDecimal("111111"));
        (new Date());
 
        OrderDTO newDTO = new OrderDTO();
        ("2");
        ("Order 2");
        ("2");
        ("Supplier Name 2");
        (new BigDecimal("222222"));
        ((new Date()));
 
        List<ComparedResult> comparedResultList = compareTwoObject(oldDTO, newDTO);
        for (ComparedResult comparedResult : comparedResultList) {
            (());
        }
 
        ("------------------------comparedResultList2---------------------------");
 
        OrderDTO orderDTO = new OrderDTO();
        ("3");
        ("Order 3");
        ("3");
        ("Supplier Name 3");
        (new BigDecimal("333333"));
        ((new Date()));
        List<ComparedResult> comparedResultList2 = compareTwoObjectCheckNull(null, orderDTO);
        for (ComparedResult comparedResult : comparedResultList2) {
            (());
        }
 
        ("------------------------comparedResultList3---------------------------");
 
        List<ComparedResult> comparedResultList3 = compareTwoObjectCheckNull(orderDTO, null);
        for (ComparedResult comparedResult : comparedResultList3) {
            (());
        }
 
    }
 
}

4. Main method, output result

true
false
false
true
true
false
------------------------BigDecimal---------------------------
false
true
true
true
false
------------------------comparedResultList---------------------------
ComparedResult{field='id', fieldName='Order id', oldValue=1, newValue=2, oldContent='1', newContent='2', pattern='', remark='1 -> 2'}
ComparedResult{field='orderCode', fieldName='Order number', oldValue=Order1, newValue=Order2, oldContent='Order1', newContent='Order2', pattern='', remark='Order1 ->Order2'}
ComparedResult{field='supplyName', fieldName='SupplyName', oldValue=SupplyName1, newValue=SupplyName2, oldContent='SupplyName1', newContent='SupplyName2', pattern='', remark='SupplyName1->SupplyName2'}
ComparedResult{field='orderAmount', fieldName='Order Amount (yuan)', 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='Order Date', 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='Order', oldValue=null, newValue=null, oldContent='Add', newContent='Order3', pattern='null', remark='Add'}
------------------------comparedResultList3---------------------------
ComparedResult{field='null', fieldName='Order', oldValue=null, newValue=null, oldContent='Order3', newContent='Delete', pattern='null', remark='Delete'}

The above is a detailed explanation of the example of Java comparing the changes of two entity class fields. For more information about Java comparing the changes of entity class fields, please pay attention to my other related articles!