目录
一、前言
我们在工作中,可能会在日志中记录数据的变化情况或者在公共处理的数据增加一个日志页面,记录每次修改的变化。我们可以根据CompareUtils工具类比较数据前后发生了怎样的变化,这样我们就可以知道数据做了哪些改变。
二、条件限制
在写这个通用方法时,我们应该考虑到以下几点:
(1)可以接收任何对象的比较,但比较的对象应该是同个对象;
(2)可以给字段进行一个备注,因为我们看到的最终内容,应该是一个中文名称;
(3)一个对象中,可以注解来获取某些字段进行比较,没有注解的不需要进行比较;
(4)一个对象中,可以使用其他字段进行比较,输出某些字段,如对字段userName进行注解,使用userId(用户id)进行比较,输出userName(用户名称);
(5)一个对象中,可以对字段进行格式化输出,如金额、日期时间等。
三、代码
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) // 注解在运行时保留,可通过反射获取
public @interface CompareAnon {
/**
* 字段中文名称
*/
String name();
/**
* 前缀字段(进行拼接,即:prefixField字段值 + name)
*/
String prefixField() default "";
/**
* 比较字段(不用原字段进行比较)
*/
String compareField() default "";
/**
* 新增、删除时,是否用该值当做“字段值”
* 如:
* 新增 -> 字段值
* 字段值 -> 删除
*/
boolean asContent() default false;
/**
* 格式
*/
String pattern() default "";
}
ComparedResult(对比结果)
import java.io.Serializable;
/**
* 对比结果
*/
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 = "供应商名称", 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 比较结果,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 对比结果列表
*/
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(); // 格式
// 前缀字段,存在(进行拼接,即: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上调用get方法等同于获得oldObject的属性值
Object oldCompareValue = getFieldValue(compareField, oldObject);
// 在newObject上调用get方法等同于获得newObject的属性值
Object newCompareValue = getFieldValue(compareField, newObject);
// 在oldObject上调用get方法等同于获得oldObject的属性值
Object oldValue = getFieldValue(fieldKey, oldObject);
// 在newObject上调用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 对比结果列表
*/
public static List<ComparedResult> compareTwoObjectCheckNull(Object oldObject, Object newObject) {
// 都为null时,不比较
if (null == oldObject && null == newObject) {
return Collections.emptyList();
}
// 新增
if (null == oldObject && null != newObject) {
// 返回新增对比结果
return addComparedResult(newObject);
}
// 删除
if (null != oldObject && null == newObject) {
// 返回删除对比结果
return deleteComparedResult(oldObject);
}
// 都不为null时,对比两个对象修改值
return compareTwoObject(oldObject, newObject);
}
/**
* 新增对比结果
* @param obj 对象
* @return 对比结果
*/
public static List<ComparedResult> addComparedResult(Object obj) {
List<ComparedResult> comparedResultList = new ArrayList<>();
Class<?> clazz = obj.getClass();
String filedName = ""; // 字段中文名称
String content = ""; // 内容
// 获取类注解
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(); // 是否用该值当做“字段值”
if (asContent) {
Object value = getFieldValue(fieldKey, obj); // 字段值
content = CastUtils.getFormatValue(value, null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 新增结果
ComparedResult result = new ComparedResult();
result.setFieldName(filedName);
result.setOldContent("新增");
result.setNewContent(content);
result.setRemark("新增");
comparedResultList.add(result);
return comparedResultList;
}
/**
* 删除对比结果
* @param obj 对象
* @return 对比结果
*/
public static List<ComparedResult> deleteComparedResult(Object obj) {
List<ComparedResult> comparedResultList = new ArrayList<>();
Class<?> clazz = obj.getClass();
String filedName = ""; // 字段中文名称
String content = ""; // 内容
// 获取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(); // 是否用该值当做“字段值”
if (asContent) {
Object value = getFieldValue(fieldKey, obj); // 字段值
content = CastUtils.getFormatValue(value, null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 删除结果
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上调用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("供应商名称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("供应商名称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("供应商名称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());
}
}
}
CastUtils (转换工具类)
import org.apache.commons.lang.StringUtils;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 转换工具类
*/
public class CastUtils {
/**
* 将obj转换为字符串列表
* @param obj obj
* @return 字符串列表
*/
public static List<String> castObjToStringList(Object obj) {
if (null == obj) {
return null;
}
List<String> result = new ArrayList<>();
if (obj instanceof List<?>) {
for (Object o : (List<?>) obj) {
result.add(o.toString());
}
} else if (obj instanceof String) {
result.add(obj.toString());
}
return result;
}
/**
* obj转list
* @param obj obj
* @param clazz clazz
* @param <T> T
* @return T
*/
public static <T> List<T> castList(Object obj, Class<T> clazz) {
if (null == obj) {
return null;
}
if (obj instanceof List<?>) {
List<T> result = new ArrayList<>();
for (Object o : (List<?>) obj) {
result.add(clazz.cast(o));
}
return result;
}
return null;
}
/**
* 获取格式化后的值
* @param value 值
* @param pattern 格式化
* @return 格式化后的值
*/
public static String getFormatValue(Object value, String pattern) {
if (null == value) {
return null;
}
String formatValue = value.toString();
if (StringUtils.isBlank(pattern)) {
return formatValue;
}
if (value instanceof Timestamp) {
value = new Date(((Timestamp) value).getTime());
}
if (value instanceof Date) {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
formatValue = sdf.format((Date) value);
}
if (value instanceof BigDecimal) {
DecimalFormat decimalFormat = new DecimalFormat(pattern);
formatValue = decimalFormat.format((BigDecimal) value);
}
return formatValue;
}
public static void main(String[] args) {
String pattern = "#,##0.00";
System.out.println(getFormatValue("5555", pattern));
System.out.println(getFormatValue(5555, pattern));
System.out.println(getFormatValue(new BigDecimal("5555"), pattern));
System.out.println(getFormatValue(new BigDecimal(5555), pattern));
}
}
四、main方法,输出结果
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='供应商名称', oldValue=供应商名称1, newValue=供应商名称2, oldContent='供应商名称1', newContent='供应商名称2', pattern='', remark='供应商名称1 -> 供应商名称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='删除'}