数据比较器,对比数据前后变化细节

2,291 阅读15分钟

前言

在开发的过程中,有时候需要对数据进行比对,来判断是否发生变化。如果一个字段一个字段比较,就太麻烦了。所以通过整合注解与反射的方式,实现一个通用的实体数据比较框架。

设计

  1. 使用注解,确定需要比较的属性。
  2. 反射获取属性与数据内容。
  3. 循环比较数据内容,并写入到结果中。
  4. 提供多种比较入参

总体结构如下:

image.png

正文

1、定义注解

1) 实体注解,确定实体名称

不是基本类型是,必须要有该注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 功能描述: 属性实体标识 <br/>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface PropertyEntity {

    /** 实体唯一标识 */
    String value();

}

2) 主键注解,校验数据是否一致

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 功能描述: 唯一标记,可以又多个,用于联合索引 <br/>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
public @interface PropertyId {
}

3) 属性描述注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 功能描述: 属性描述 <br/>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyField {

    /** 中文描述 */
    String name() default "";

    /** 排序字段,与@PropertyOrder可以同时使用,取两个最大的为主 */
    float order() default 0.00F;

}

4) 顺序注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 功能描述: 属性排序 <br/>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyOrder {

    /** 排序值 */
    float value() default 0.00F;

}

5) 排除注解,不进行比较

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 功能描述: 属性忽略比较 <br/>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyIgnore {

}

6) 自定义比较器

如果有特殊比较方式,则自行定义比较器

import com.cah.project.compare.comparator.DefaultComparator;
import com.cah.project.compare.comparator.IComparator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 功能描述: 属性比较器,可以自定义 <br/>
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface PropertyComparator {

    /** 比较器,默认比较器 */
    Class<? extends IComparator> compare() default DefaultComparator.class;

}

2、自定义比较器

1) 比较器接口

/**
 * 功能描述: 比较器接口 <br/>
 */
public interface IComparator<T> {

    /**
     * 功能描述: 对象比较 <br/>
     *
     * @param t1 对象1
     * @param t2 对象2
     * @return "int" 返回比较结果 0-相同;非0-不同
     */
    int compare(T t1, T t2);

}

2) 默认比较器实现

import java.util.Date;
import java.util.Objects;

/**
 * 功能描述: 默认比较器 <br/>
 */
public class DefaultComparator implements IComparator<Object> {

    @Override
    public int compare(Object o1, Object o2) {
        // 同时为空,为相同
        if(Objects.isNull(o1) && Objects.isNull(o2)) {
            return 0;
        }
        // 都不为空
        if(!Objects.isNull(o2) && !Objects.isNull(o1)) {
            if(o1 instanceof Date) {
                return ((Date) o1).compareTo((Date) o2);
            } else {
                if(o1 == o2 || o1.equals(o2)) {
                    return 0;
                }
            }
            return -1;
        }
        return -1;
    }
}

3、异常类

import com.cah.project.compare.enums.ExceptionEnum;
import lombok.AllArgsConstructor;

/**
 * 功能描述: 比较异常类 <br/>
 */
@AllArgsConstructor
public class CompareException extends RuntimeException {

    private final String code;
    private final String desc;

    public CompareException(ExceptionEnum ee) {
        this(ee.getCode(), ee.getDesc());
    }

    public CompareException(ExceptionEnum ee, Object... args) {
        this(ee.getCode(), String.format(ee.getDesc(), args));
    }

}

4、枚举定义

1) 变化类型:新增,修改,删除,无变化等四种情况

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 功能描述: 变化类型枚举 <br/>
 */
@Getter
@AllArgsConstructor
public enum ChangeTypeEnum {

    ADDED("1", "新增"),
    REMOVED("2", "删除"),
    MODIFIED("3", "修改"),
    UNCHANGED("4", "无变化"),
    ;

    private final String code;
    private final String desc;

}

2) 模型类型枚举

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 功能描述: 模型类型 <br/>
 */
@Getter
@AllArgsConstructor
public enum ModelTypeEnum {

    ENTITY("Entity", "实体"),
    PROPERTY("Property", "基础属性"),
    ENTITY_PROPERTY("EntityProperty", "实体属性"),
    LIST_PROPERTY("ListProperty", "列表属性"),
    MAP_PROPERTY("MapProperty", "Map属性"),
    ;

    private final String code;
    private final String desc;
}

3) 异常枚举

import lombok.AllArgsConstructor;
import lombok.Getter;

/**
 * 功能描述: 异常枚举 <br/>
 */
@Getter
@AllArgsConstructor
public enum ExceptionEnum {

    OVER_DEPTH("0", "数据结构深度超过指定范围"),
    INCONSISTENT_CLASS("1", "比较的对象类型不一致"),
    PROPERTY_ENTITY_NULL("2", "比较的对象必须拥有@PropertyEntity注解"),
    PROPERTY_ID_NULL("3", "比较的对象必须拥有@PropertyId注解"),
    PROPERTY_ID_TYPE("4", "对象%s的@PropertyId注解类型必须为String或Long"),
    PROPERTY_ID_VALUE_NULL("5", "对象%s属性%s的@PropertyId注解的值为空"),
    ;

    private final String code;
    private final String desc;
}

4) 实体类型枚举

这里使用了枚举+单例的模式。这里为什么不使用策略枚举的原因,在 AnalyzeUtil中需要做属性类型的判断,不方便使用。

import com.cah.project.compare.process.IPropertyProcess;
import com.cah.project.compare.process.impl.BaseTypeProcess;
import com.cah.project.compare.process.impl.EntityTypeProcess;
import com.cah.project.compare.process.impl.ListTypeProcess;
import com.cah.project.compare.process.impl.MapTypeProcess;
import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;
import java.util.Map;

/**
 * 功能描述: 实体类型枚举 <br/>
 */
@Getter
@AllArgsConstructor
public enum PropertyTypeEnum {

    BASE_TYPE("base", "基础数据类型(int/String/...)", new BaseTypeProcess()),
    LIST_TYPE(List.class.getTypeName(), "List", new ListTypeProcess()),
    MAP_TYPE(Map.class.getTypeName(), "Map", new MapTypeProcess()),
    ENTITY_OBJECT_TYPE("entityObject", "自定义实体对象", new EntityTypeProcess()),
    ;

    private final String typeName;
    private final String desc;
    // 处理器
    private final IPropertyProcess process;

}

5、处理器,与实体类型枚举一起使用

1) 处理器接口

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;

/**
 * 功能描述: 实体过程 <br/>
 */
public interface IPropertyProcess {

    /**
     * 功能描述: 单个对象处理 <br/>
     *
     * @param pm 属性模型
     * @param cte 变化类型
     * @return "com.cah.project.compare.model.ChangeModel"
     */
    ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException;

    /**
     * 功能描述: 两个对象处理 <br/>
     *
     * @param beforePm before属性模型
     * @param afterPm after属性模型
     * @return "com.cah.project.compare.model.ChangeModel"
     */
    ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException;

}

2) 处理器抽象类

将共有的方法封装在这里,方便各个真实处理器继承使用

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;

import java.util.Objects;

public abstract class AbsProcess implements IPropertyProcess {

    protected abstract ModelTypeEnum getModelType();

    /**
     * 功能描述: 获取基本的类型 <br/>
     *
     * @param pm 属性模型
     * @param cte 变化类型
     * @return "com.compare.model.ChangeModel"
     */
    protected ChangeModel getBaseChangeModel(PropertyModel pm, ChangeTypeEnum cte) {
        ChangeModel cm = getBaseChangeModel(pm);
        cm.setChangeType(cte);
        if(!Objects.isNull(pm.getValue())) {
            cm.setTypeValue(pm.getValue().toString());
        }
        // 删除的,说明before有,after没有
        cm.setBefore(ChangeTypeEnum.REMOVED.equals(cte) ? pm.getValue() : null);
        // 新增的,说明after有,before没有
        cm.setAfter(ChangeTypeEnum.ADDED.equals(cte) ? pm.getValue() : null);
        return cm;
    }

    /**
     * 功能描述: 获取基本的类型 <br/>
     *
     * @param beforePm 改变前属性模型
     * @param afterPm 改变后属性模型
     * @return "com.compare.model.ChangeModel"
     */
    protected ChangeModel getBaseChangeModel(PropertyModel beforePm, PropertyModel afterPm) {
        ChangeModel cm = getBaseChangeModel(beforePm);
        // 删除的,说明before有,after没有
        cm.setBefore(beforePm.getValue());
        // 新增的,说明after有,before没有
        cm.setAfter(afterPm.getValue());
        return cm;
    }

    /**
     * 功能描述: 获取基本的类型 <br/>
     *
     * @param pm 属性模型
     * @return "com.compare.model.ChangeModel"
     */
    protected ChangeModel getBaseChangeModel(PropertyModel pm) {
        ChangeModel cm = new ChangeModel();
        cm.setTypeName(pm.getName());
        cm.setTypeComment(pm.getPropertyName());
        cm.setModelType(getModelType());
        return cm;
    }

    /**
     * 功能描述: 比较对象 <br/>
     *
     * @param cm 变化模型
     * @param beforePm before
     * @param afterPm after
     */
    protected void compareChangeType(ChangeModel cm, PropertyModel beforePm, PropertyModel afterPm) {
        if(beforePm.getComparator().compare(beforePm.getValue(), afterPm.getValue()) == 0) {
            cm.setChangeType(ChangeTypeEnum.UNCHANGED);
        } else {
            cm.setChangeType(ChangeTypeEnum.MODIFIED);
        }
    }

}

3) 基本类型处理器

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;

/**
 * 功能描述: 基本类型处理 <br/>
 */
public class BaseTypeProcess extends AbsProcess {

    @Override
    protected ModelTypeEnum getModelType() {
        return ModelTypeEnum.PROPERTY;
    }

    @Override
    public ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) {
        return getBaseChangeModel(pm, cte);
    }

    @Override
    public ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) {
        ChangeModel cm = getBaseChangeModel(beforePm, afterPm);
        compareChangeType(cm, beforePm, afterPm);
        return cm;
    }

}

4) 实体类型处理器

需要与 CompareHelper配合使用,可能存在实体套实体的情况,会产生递归。

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;
import com.cah.project.compare.util.CompareHelper;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 功能描述: 自定义实体类型处理 <br/>
 */
public class EntityTypeProcess extends AbsProcess {

    @Override
    protected ModelTypeEnum getModelType() {
        return ModelTypeEnum.ENTITY_PROPERTY;
    }

    @Override
    public ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException {
        ChangeModel cm = getBaseChangeModel(pm, cte);
        // 设置子节点
        if(!Objects.isNull(pm.getValue())) {
            List<ChangeModel> children = new ArrayList<>();
            ChangeModel child = CompareHelper.assemblyChangeModelObj(pm.getValue(), cte);
            children.add(child);
            cm.setChildren(children);
        }
        return cm;
    }

    @Override
    public ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException {
        ChangeModel cm = getBaseChangeModel(beforePm, afterPm);
        // 如果两个都为空,则为没变化
        if(beforePm.getValue() == null && afterPm.getValue() == null) {
            cm.setChangeType(ChangeTypeEnum.UNCHANGED);
        }
        // 如果before的children不为空,after的为空,则为删除
        if(beforePm.getValue() != null && afterPm.getValue() == null) {
            cm.setChangeType(ChangeTypeEnum.REMOVED);
            List<ChangeModel> children = new ArrayList<>();
            ChangeModel child = CompareHelper.assemblyChangeModelObj(beforePm.getValue(), ChangeTypeEnum.REMOVED);
            children.add(child);
            cm.setChildren(children);
        }
        // 如果before的children为空,after的不为空,则为新增
        if(beforePm.getValue() == null && afterPm.getValue() != null) {
            cm.setChangeType(ChangeTypeEnum.ADDED);
            List<ChangeModel> children = new ArrayList<>();
            ChangeModel child = CompareHelper.assemblyChangeModelObj(afterPm.getValue(), ChangeTypeEnum.ADDED);
            children.add(child);
            cm.setChildren(children);
        }
        // 如果两个都不为空,则重新调用比较
        if(beforePm.getValue() != null && afterPm.getValue() != null) {
            if(beforePm.getComparator() != null) {
                compareChangeType(cm, beforePm, afterPm);
            } else {
                // 默认未变化
                cm.setChangeType(ChangeTypeEnum.UNCHANGED);
                // 设置子节点
                List<ChangeModel> children = new ArrayList<>();
                ChangeModel child = CompareHelper.assemblyChangeModelObj(beforePm.getKey(), beforePm.getValue(), afterPm.getValue());
                children.add(child);
                cm.setChildren(children);
                if(!children.isEmpty()) {
                    // 根据子信息,重新设置变化类型
                    cm.setChangeType(CompareHelper.getChildrenChangeType(children));
                }
            }
        }
        return cm;
    }
}

5) List处理器

因为List中一般是对象实体,所以需要用到 实体类型处理器,还要使用解析工具AnalyzeUtil,也会产生递归调用。

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;
import com.cah.project.compare.util.AnalyzeUtil;
import com.cah.project.compare.util.CompareHelper;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 功能描述: List类型处理 <br/>
 */
public class ListTypeProcess extends AbsProcess {

    @Override
    protected ModelTypeEnum getModelType() {
        return ModelTypeEnum.LIST_PROPERTY;
    }

    @Override
    public ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException {
        ChangeModel cm = getBaseChangeModel(pm, cte);
        if(!Objects.isNull(pm.getValue())) {
            // 设置子节点
            List<ChangeModel> children = new ArrayList<>();
            // 继续比较列表
            Map<String, Object> stringObjectMap = AnalyzeUtil.toMap((List) pm.getValue());
            CompareHelper.assemblyChangeModelList(children,
                    ChangeTypeEnum.REMOVED.equals(cte) ? stringObjectMap : null,
                    ChangeTypeEnum.ADDED.equals(cte) ? stringObjectMap : null);
            cm.setChildren(children);
        }
        return cm;
    }

    @Override
    public ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException {
        ChangeModel cm = getBaseChangeModel(beforePm, afterPm);
        // 如果两个都为空,则为没变化
        if(beforePm.getValue() == null && afterPm.getValue() == null) {
            cm.setChangeType(ChangeTypeEnum.UNCHANGED);
        }
        // 如果before的children不为空,after的为空,则为删除
        if(beforePm.getValue() != null && afterPm.getValue() == null) {
            cm.setChangeType(ChangeTypeEnum.REMOVED);
            // 设置子节点
            List<ChangeModel> children = new ArrayList<>();
            // 继续比较列表
            Map<String, Object> stringObjectMap = AnalyzeUtil.toMap((List) beforePm.getValue());
            CompareHelper.assemblyChangeModelList(children, stringObjectMap, null);
            cm.setChildren(children);
        }
        // 如果before的children为空,after的不为空,则为新增
        if(beforePm.getValue() == null && afterPm.getValue() != null) {
            cm.setChangeType(ChangeTypeEnum.ADDED);
            // 设置子节点
            List<ChangeModel> children = new ArrayList<>();
            // 继续比较列表
            Map<String, Object> stringObjectMap = AnalyzeUtil.toMap((List) afterPm.getValue());
            CompareHelper.assemblyChangeModelList(children, null, stringObjectMap);
            cm.setChildren(children);
        }
        // 如果两个都不为空,则重新调用比较
        if(beforePm.getValue() != null && afterPm.getValue() != null) {
            // 默认未变化
            cm.setChangeType(ChangeTypeEnum.UNCHANGED);
            // 设置子节点
            List<ChangeModel> children = new ArrayList<>();
            // 继续比较列表
            Map<String, Object> beforeObjMap = AnalyzeUtil.toMap((List) beforePm.getValue());
            Map<String, Object> afterObjMap = AnalyzeUtil.toMap((List) afterPm.getValue());
            CompareHelper.assemblyChangeModelList(children, beforeObjMap, afterObjMap);
            cm.setChildren(children);
            if(!children.isEmpty()) {
                // 根据子信息,重新设置变化类型
                cm.setChangeType(CompareHelper.getChildrenChangeType(children));
            }
        }
        return cm;
    }
}

5) Map处理器

如果在设计模型的过程中使用到了Map,要考虑一下,是不是可以使用实体进行定义。所以这个处理器就自由发挥吧,没有开发。

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;
import com.cah.project.compare.process.AbsProcess;

/**
 * 功能描述: Map类型处理 <br/>
 */
public class MapTypeProcess extends AbsProcess {
    @Override
    protected ModelTypeEnum getModelType() {
        return ModelTypeEnum.MAP_PROPERTY;
    }

    @Override
    public ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException {
        ChangeModel cm = getBaseChangeModel(pm, cte);
        // TODO ...
        return cm;
    }

    @Override
    public ChangeModel process(PropertyModel beforePm, PropertyModel afterPm) throws InstantiationException, IllegalAccessException {
        ChangeModel cm = getBaseChangeModel(beforePm, afterPm);
        // TODO ...
        return cm;
    }
}

6、模型定义

1) 实体解析模型

解析需要比较的实体,进行存储。

import com.cah.project.compare.comparator.IComparator;
import com.cah.project.compare.enums.PropertyTypeEnum;
import lombok.Data;

import java.lang.reflect.Type;

/**
 * 功能描述: 属性模型 <br/>
 */
@Data
public class PropertyModel {
    /** 如果是object,则key,否则与value一致 */
    private String key;
    /** 属性名 */
    private String name;
    /** 属性值 */
    private Object value;
    /** 设置属性描述 */
    private String propertyName;
    /** 属性所属的类 */
    private Class<?> declaring;
    /** 属性类型 */
    private Type type;
    /** 属性类型枚举 */
    private PropertyTypeEnum pte;
    /** 属性比较器 */
    private IComparator comparator;
    /** 所属实体标识 */
    private String propertyEntity;
    /** 排序 */
    private float order;
}

2) 变化模型

最终每条数据变化情况

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import lombok.Data;

import java.util.List;

/**
 * 功能描述: 改变模型 <br/>
 */
@Data
public class ChangeModel {

    /** 类型名 */
    private String typeName;
    /** 类型值 */
    private String typeValue;
    /** 类型描述(如果为对象,则是PropertyEntity,如果是属性,则为PropertyName) */
    private String typeComment;
    /** 变化类型 */
    private ChangeTypeEnum changeType;
    /** 改变前的数据 */
    private Object before;
    /** 改变后的数据 */
    private Object after;
    /** 子节点 */
    private List<ChangeModel> children;
    /** 模型类型 */
    private ModelTypeEnum modelType;
}

7、解析工具 AnalyzeUtil

对比较对象进行解析的工具类

import com.cah.project.compare.annotation.*;
import com.cah.project.compare.comparator.DefaultComparator;
import com.cah.project.compare.enums.ExceptionEnum;
import com.cah.project.compare.enums.PropertyTypeEnum;
import com.cah.project.compare.exception.CompareException;
import com.cah.project.compare.model.PropertyModel;

import java.lang.reflect.Field;
import java.util.*;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

/**
 * 功能描述: 解析工具 <br/>
 */
public class AnalyzeUtil {

    /** Key连接符 */
    private static final String KEY_LINK = "|_|";

    /**
     * 功能描述: 将List转换成Map,key为List中对象的 唯一标识(@PropertyId注解) <br/>
     *
     * @param cs Collection集合
     * @return "java.util.Map<java.lang.String,java.lang.Object>"
     */
    public static Map<String, Object> toMap(Collection<?> cs) throws IllegalAccessException {
        Map<String, Object> map = new HashMap<>();
        if(cs != null && !cs.isEmpty()) {
            for(Object o : cs) {
                // 如果是基本类型,则直接toString
                String key = isBaseType(o.getClass().getName()) ? o.toString() : getKey(o);
                map.put(key, o);
            }
        }
        return map;
    }

    /**
     * 功能描述: 将对象转成map,key为属性名,value为对象映射成的PropertyModel模型 <br/>
     *
     * @param o 对象
     * @return "java.util.Map<java.lang.String,com.compare.model.PropertyModel>"
     */
    public static Map<String, PropertyModel> toMap(Object o) throws IllegalAccessException, InstantiationException {
        if(Objects.isNull(o)) {
            return null;
        }
        // 获取全部字段
        Field[] fields = o.getClass().getDeclaredFields();
        List<PropertyModel> list = new ArrayList<>(fields.length);
        String propertyEntity = getPropertyEntity(o);
        String key = getKey(o);
        for (Field field : fields) {
            // 非忽略注解
            if(!isIgnore(field)) {
                field.setAccessible(true);
                PropertyModel pm = new PropertyModel();
                pm.setKey(key);
                // 设置属性信息
                pm.setName(field.getName());
                pm.setValue(field.get(o));
                // 设置类型
                pm.setType(field.getType());
                // 设置属性类型枚举
                if(isBaseType(pm.getType().getTypeName())) {
                    pm.setPte(PropertyTypeEnum.BASE_TYPE);
                } else if(isList(pm.getType().getTypeName())) {
                    pm.setPte(PropertyTypeEnum.LIST_TYPE);
                } else if(isMap(pm.getType().getTypeName())) {
                    pm.setPte(PropertyTypeEnum.MAP_TYPE);
                } else {
                    pm.setPte(PropertyTypeEnum.ENTITY_OBJECT_TYPE);
                }
                PropertyField propertyField = field.getAnnotation(PropertyField.class);
                if(propertyField != null) {
                    pm.setPropertyName(propertyField.name());
                    pm.setOrder(propertyField.order());
                }
                PropertyOrder propertyOrder = field.getAnnotation(PropertyOrder.class);
                if(propertyOrder != null) {
                    // 两个注解都有,哪个大取哪个
                    pm.setOrder(Math.max(pm.getOrder(), propertyOrder.value()));
                }
                // 设置比较器
                PropertyComparator pc = field.getAnnotation(PropertyComparator.class);
                if(pc != null) {
                    pm.setComparator(pc.compare().newInstance());
                } else {
                    if(PropertyTypeEnum.BASE_TYPE.equals(pm.getPte())) {
                        pm.setComparator(new DefaultComparator());
                    }
                }
                pm.setDeclaring(field.getDeclaringClass());
                pm.setPropertyEntity(propertyEntity);
                list.add(pm);
            }
        }
        // 转成有序map
        return list.stream().sorted(Comparator.comparing(PropertyModel::getOrder))
                .collect(Collectors.toMap(PropertyModel::getName, e -> e, throwingMerger(), LinkedHashMap::new));
    }

    /**
     * 功能描述: 异常处理 <br/>
     *
     * @return "java.util.function.BinaryOperator<T>"
     */
    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }

    /**
     * 功能描述: 确定唯一标识数据 <br/>
     *
     * @param o 对象
     * @return "java.lang.String"
     */
    public static String getKey(Object o) throws IllegalAccessException {
        if(isBaseType(o.getClass().getName())) {
            return o.toString();
        }
        // 获取全部字段
        Field[] fields = o.getClass().getDeclaredFields();
        StringBuilder sb = new StringBuilder();
        for(Field field : fields) {
            // 非忽略注解
            if(!isIgnore(field)) {
                // 获取@PropertyId注解
                PropertyId id = field.getAnnotation(PropertyId.class);
                if(id != null) {
                    // 校验是否基本类型
                    String typeName = field.getType().getTypeName();
                    checkPropertyIdType(typeName, o);
                    field.setAccessible(true);
                    if(field.get(o) == null || "".equals(field.get(o))) {
                        throw new CompareException(ExceptionEnum.PROPERTY_ID_VALUE_NULL, o.getClass().getName(), field.getName());
                    }
                    sb.append(field.get(o).toString());
                    sb.append(KEY_LINK);
                }
            }
        }
        if(sb.length() <= 0) {
            throw new CompareException(ExceptionEnum.PROPERTY_ID_NULL);
        }
        return sb.substring(0, sb.lastIndexOf(KEY_LINK));
    }

    /**
     * 功能描述: 获取实体标识注解内容 <br/>
     *
     * @param o 对象
     * @return "java.lang.String"
     */
    public static String getPropertyEntity(Object o) {
        return getPropertyEntity(o.getClass());
    }

    /**
     * 功能描述: 获取实体标识注解内容 <br/>
     *
     * @param clazz 类
     * @return "java.lang.String"
     */
    public static String getPropertyEntity(Class<?> clazz) {
        if(isBaseType(clazz.getName())) {
            return "";
        }
        PropertyEntity pe = clazz.getAnnotation(PropertyEntity.class);
        if(pe == null) {
            // 不为基本类型时,必须要有PropertyEntity注解
            throw new CompareException(ExceptionEnum.PROPERTY_ENTITY_NULL);
        }
        return pe.value();
    }

    /**
     * 功能描述: 是忽略注解字段 <br/>
     *
     * @param field 字段
     * @return "boolean" true-是;false-不是
     */
    private static boolean isIgnore(Field field) {
        return field.getAnnotation(PropertyIgnore.class) != null;
    }

    /**
     * 功能描述: 是否列表 <br/>
     * 
     * @param typeName 类型名称
     * @return "boolean"
     */
    private static boolean isList(String typeName) {
        return List.class.getTypeName().equals(typeName);
    }

    /**
     * 功能描述: 是否Map <br/>
     *
     * @param typeName 类型名称
     * @return "boolean"
     */
    private static boolean isMap(String typeName) {
        return Map.class.getTypeName().equals(typeName);
    }

    /**
     * 功能描述: 是否基本类型 <br/>
     *
     * @param className 类
     * @return "boolean" true-是;false-不是
     */
    private static boolean isBaseType(String className) {
        if(className.equals(Integer.class.getName()) ||
                className.equals(int.class.getName()) ||
                className.equals(Byte.class.getName()) ||
                className.equals(byte.class.getName()) ||
                className.equals(Long.class.getName()) ||
                className.equals(long.class.getName()) ||
                className.equals(Double.class.getName()) ||
                className.equals(double.class.getName()) ||
                className.equals(Float.class.getName()) ||
                className.equals(float.class.getName()) ||
                className.equals(Character.class.getName()) ||
                className.equals(char.class.getName()) ||
                className.equals(Short.class.getName()) ||
                className.equals(short.class.getName()) ||
                className.equals(java.math.BigDecimal.class.getName()) ||
                className.equals(java.math.BigInteger.class.getName()) ||
                className.equals(Boolean.class.getName()) ||
                className.equals(boolean.class.getName()) ||
                className.equals(String.class.getName())) {
            return true;
        }
        return false;
    }

    /**
     * 功能描述: 基本类型校验 <br/>
     */
    private static void checkPropertyIdType(String typeName, Object o) {
        if(!"java.lang.String".equals(typeName)
                && !"java.lang.Long".equals(typeName)
                && !"java.lang.Integer".equals(typeName)
                && !"long".equals(typeName)
                && !"int".equals(typeName)) {
            throw new CompareException(ExceptionEnum.PROPERTY_ID_TYPE, o.getClass().getName());
        }
    }
}

8、数据比较核心类

唯一提供对外的方法为 compareProcess,接收两个数据列表。然后对数据进行比较,返回

import com.cah.project.compare.enums.ChangeTypeEnum;
import com.cah.project.compare.enums.ModelTypeEnum;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.model.PropertyModel;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 功能描述: 比较帮助类 <br/>
 */
public class CompareHelper {

    /**
     * 功能描述: 数据比较(提供给外部使用) <br/>
     */
    public static void compareProcess(List<ChangeModel> changeList, Collection<?> before, Collection<?> after) throws IllegalAccessException, InstantiationException {
        if((before == null || before.isEmpty()) && (after == null || after.isEmpty())) {
            return;
        }
        Map<String, Object> afterObjMap = null, beforeObjMap = null;
        if(before == null || before.isEmpty()) {
            // 全部为新增
            // 转换为Map<String, Object> key:主键值,value:对象信息
            afterObjMap = AnalyzeUtil.toMap(after);
        }
        if(after == null || after.isEmpty()) {
            // 全部为删除
            beforeObjMap = AnalyzeUtil.toMap(before);
        }
        if(before != null && !before.isEmpty() && after != null && !after.isEmpty()) {
            // 修改或者不变
            beforeObjMap = AnalyzeUtil.toMap(before);
            afterObjMap = AnalyzeUtil.toMap(after);
        }
        // 组转成changeModel
        assemblyChangeModelList(changeList, beforeObjMap, afterObjMap);
    }

    /**
     * 功能描述: 组装 <br/>
     *
     * @param changeList 变化列表
     * @param beforeObjMap before
     * @param afterObjMap after
     */
    public static void assemblyChangeModelList(List<ChangeModel> changeList,
                                            Map<String, Object> beforeObjMap,
                                            Map<String, Object> afterObjMap) throws IllegalAccessException, InstantiationException {
        if(beforeObjMap == null) {
            Set<Map.Entry<String, Object>> entries = afterObjMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                ChangeModel cm = assemblyChangeModelObj(entry.getValue(), ChangeTypeEnum.ADDED);
                changeList.add(cm);
            }
            return;
        }
        if(afterObjMap == null) {
            Set<Map.Entry<String, Object>> entries = beforeObjMap.entrySet();
            for (Map.Entry<String, Object> entry : entries) {
                ChangeModel cm = assemblyChangeModelObj(entry.getValue(), ChangeTypeEnum.REMOVED);
                changeList.add(cm);
            }
            return;
        }
        Set<Map.Entry<String, Object>> beforeEntries = beforeObjMap.entrySet();
        for(Map.Entry<String, Object> beforeEntry : beforeEntries) {
            ChangeModel cm;
            if (!afterObjMap.containsKey(beforeEntry.getKey())) {
                // 如果在after中不存在key,则为删除
                cm = assemblyChangeModelObj(beforeEntry.getValue(), ChangeTypeEnum.REMOVED);
            } else {
                // 继续判断是不变,还是修改
                cm = assemblyChangeModelObj(beforeEntry.getKey(), beforeEntry.getValue(), afterObjMap.get(beforeEntry.getKey()));
            }
            changeList.add(cm);
        }
        // 如果after中有before没有的值,则为新增
        Set<Map.Entry<String, Object>> afterEntries = afterObjMap.entrySet();
        for (Map.Entry<String, Object> afterEntry : afterEntries) {
            if(!beforeObjMap.containsKey(afterEntry.getKey())) {
                ChangeModel cm = assemblyChangeModelObj(afterEntry.getValue(), ChangeTypeEnum.ADDED);
                changeList.add(cm);
            }
        }
    }

    /**
     * 功能描述: 组转对象 <br/>
     *
     * @param key 改变前
     * @param before 改变前
     * @param after 改变后
     * @return "com.compare.model.ChangeModel"
     */
    public static ChangeModel assemblyChangeModelObj(String key, Object before, Object after) throws IllegalAccessException, InstantiationException {
        ChangeModel cm = new ChangeModel();
        // 默认不变
        cm.setChangeType(ChangeTypeEnum.UNCHANGED);
        cm.setModelType(ModelTypeEnum.ENTITY);
        cm.setBefore(before);
        cm.setAfter(after);
        // 设置数据主键
        cm.setTypeValue(key);
        // 设置类名
        cm.setTypeName(before.getClass().getSimpleName());
        cm.setTypeComment(AnalyzeUtil.getPropertyEntity(before));
        // 转换属性
        Map<String, PropertyModel> beforeFiledMap = AnalyzeUtil.toMap(before);
        Map<String, PropertyModel> afterFiledMap = AnalyzeUtil.toMap(after);
        if(beforeFiledMap != null && afterFiledMap != null) {
            // 比较属性是否一致
            List<ChangeModel> children = new ArrayList<>();
            // 同一个对象下面,字段属性肯定一致,缩编去一个key就行
            Set<Map.Entry<String, PropertyModel>> beforeEntries = beforeFiledMap.entrySet();
            for (Map.Entry<String, PropertyModel> beforeEntry : beforeEntries) {
                // 获取属性key
                String propertyKey = beforeEntry.getKey();
                // 通过属性类型,对比两个数据
                ChangeModel child = beforeEntry.getValue().getPte().getProcess().process(beforeEntry.getValue(), afterFiledMap.get(propertyKey));
                children.add(child);
            }
            cm.setChildren(children);
        }
        if(cm.getChildren() != null && !cm.getChildren().isEmpty()) {
            cm.setChangeType(getChildrenChangeType(cm.getChildren()));
        }
        return cm;
    }

    /**
     * 功能描述: 获取子属性的最终变化类型 <br/>
     *
     * @param children 子变化模型
     * @return "com.compare.enums.ChangeTypeEnum"
     */
    public static ChangeTypeEnum getChildrenChangeType(List<ChangeModel> children) {
        // 获取children中是否全部为的变化类型
        List<ChangeTypeEnum> changeTypes = children.stream().map(ChangeModel::getChangeType).distinct().collect(Collectors.toList());
        if(changeTypes.size() == 1 && ChangeTypeEnum.UNCHANGED.equals(changeTypes.get(0))) {
            return ChangeTypeEnum.UNCHANGED;
        } else {
            return ChangeTypeEnum.MODIFIED;
        }
    }

    /**
      * 功能描述: 组装单个对象 <br/>
      *
      * @param obj 对象
      * @param cte 变化类型
      * @return "com.compare.model.ChangeModel"
      */
    public static ChangeModel assemblyChangeModelObj(Object obj, ChangeTypeEnum cte) throws IllegalAccessException, InstantiationException {
        ChangeModel cm = new ChangeModel();
        // 设置变化类型
        cm.setChangeType(cte);
        cm.setModelType(ModelTypeEnum.ENTITY);
        // 删除的,说明before有,after没有
        cm.setBefore(ChangeTypeEnum.REMOVED.equals(cte) ? obj : null);
        // 新增的,说明after有,before没有
        cm.setAfter(ChangeTypeEnum.ADDED.equals(cte) ? obj : null);
        // 设置数据主键
        cm.setTypeValue(AnalyzeUtil.getKey(obj));
        // 设置类名
        cm.setTypeName(obj.getClass().getSimpleName());
        cm.setTypeComment(AnalyzeUtil.getPropertyEntity(obj));
        // 转换属性
        Map<String, PropertyModel> filedMap = AnalyzeUtil.toMap(obj);
        if(filedMap != null) {
            List<ChangeModel> children = new ArrayList<>();
            Set<Map.Entry<String, PropertyModel>> entries = filedMap.entrySet();
            for (Map.Entry<String, PropertyModel> entry : entries) {
                // 拼接字段
                ChangeModel cmc = entry.getValue().getPte().getProcess().process(entry.getValue(), cte);
                if(cmc != null) {
                    children.add(cmc);
                }
            }
            cm.setChildren(children);
        }
        return cm;
    }
}

9、提供对外调用类 CompareCore

所有需要对数据进行比较的,都调用该类的方法。

import com.cah.project.compare.enums.ExceptionEnum;
import com.cah.project.compare.exception.CompareException;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.util.CompareHelper;

import java.util.*;

/**
 * 功能描述: 比较核心方法 <br/>
 */
public class CompareCore {

    /**
     * 功能描述: 列表比较 <br/>
     *
     * @param before 之前
     * @param after 之后
     */
    public static <E> List<ChangeModel> compare(Collection<E> before, Collection<E> after) throws IllegalAccessException, InstantiationException {
        List<ChangeModel> changeList = new ArrayList<>();
        CompareHelper.compareProcess(changeList, before, after);
        return changeList;
    }

    /**
     * 功能描述: 对象比较 <br/>
     *
     * @param before 之前
     * @param after 之后
     * @return "java.util.List<com.cah.project.compare.model.ChangeModel>"
     */
    public static List<ChangeModel> compare(Object before, Object after) throws IllegalAccessException, InstantiationException {
        // 校验 o1 和 o2 的类型是一致的
        if(!before.getClass().equals(after.getClass())) {
            throw new CompareException(ExceptionEnum.INCONSISTENT_CLASS);
        }
        return compare(Collections.singletonList(before), Collections.singletonList(after));
    }
    
}

测试

import com.cah.project.compare.CompareCore;
import com.cah.project.compare.model.ChangeModel;
import com.cah.project.compare.test.entity.User;

import java.util.ArrayList;
import java.util.List;

public class CompareTest {

    public static void main(String[] args) throws Exception {
        List<ChangeModel> compare = CompareCore.compare(getUserListLeft().get(1), getUserListRight().get(1));
        System.out.println(compare);
    }

    private static List<User> getUserListLeft() {
        List<User> list = new ArrayList<>();
        User user = new User();
        user.setIdCard("11111");
        user.setName("张三");
        list.add(user);

        User user1 = new User();
        user1.setIdCard("11112");
        user1.setName("李四");

        User user11 = new User();
        user11.setIdCard("1122");
        user11.setName("利佩欧");
        user1.setSpouse(user11);

        List<User> children = new ArrayList<>();
        User user111 = new User();
        user111.setIdCard("11221");
        user111.setName("利斯海1");
        children.add(user111);
        user1.setChildren(children);

        User user112 = new User();
        user112.setIdCard("11222");
        user112.setName("利斯海2");
        children.add(user112);
        user1.setChildren(children);
        list.add(user1);

        return list;
    }

    private static List<User> getUserListRight() {
        List<User> list = new ArrayList<>();
        User user = new User();
        user.setIdCard("11111");
        user.setName("张三");
        list.add(user);

        User user1 = new User();
        user1.setIdCard("11112");
        user1.setName("李四");

        User user11 = new User();
        user11.setIdCard("1122");
        user11.setName("利佩欧");
        user1.setSpouse(user11);

        List<User> children = new ArrayList<>();
        User user111 = new User();
        user111.setIdCard("11221");
        user111.setName("利斯海1");
        children.add(user111);
        user1.setChildren(children);

        User user112 = new User();
        user112.setIdCard("11222");
        user112.setName("利斯海22");
        children.add(user112);
        user1.setChildren(children);
        list.add(user1);

        return list;
    }

结果

可以看出,只要子项有变化,主项的最终结论也是变化。 image.png

[ChangeModel(typeName=User, typeValue=11112, typeComment=用户信息, changeType=MODIFIED, before=User(idCard=11112, name=李四, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海2, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)]), after=User(idCard=11112, name=李四, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海22, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)]), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=11112, after=11112, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=UNCHANGED, before=李四, after=李四, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=User, typeValue=11112, typeComment=用户信息, changeType=UNCHANGED, before=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=1122, name=利佩欧, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=1122, after=1122, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=UNCHANGED, before=利佩欧, after=利佩欧, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=null, after=null, children=null, modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=UNCHANGED, before=null, after=null, children=null, modelType=LIST_PROPERTY)], modelType=ENTITY)], modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=MODIFIED, before=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海2, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)], after=[User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), User(idCard=11222, name=利斯海22, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null)], children=[ChangeModel(typeName=User, typeValue=11221, typeComment=用户信息, changeType=UNCHANGED, before=User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=11221, name=利斯海1, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=11221, after=11221, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=UNCHANGED, before=利斯海1, after=利斯海1, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=null, after=null, children=null, modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=UNCHANGED, before=null, after=null, children=null, modelType=LIST_PROPERTY)], modelType=ENTITY), ChangeModel(typeName=User, typeValue=11222, typeComment=用户信息, changeType=MODIFIED, before=User(idCard=11222, name=利斯海2, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), after=User(idCard=11222, name=利斯海22, age=null, weight=null, stature=0.0, gender=0, totalDeposit=null, spouse=null, children=null), children=[ChangeModel(typeName=idCard, typeValue=null, typeComment=身份证, changeType=UNCHANGED, before=11222, after=11222, children=null, modelType=PROPERTY), ChangeModel(typeName=name, typeValue=null, typeComment=姓名, changeType=MODIFIED, before=利斯海2, after=利斯海22, children=null, modelType=PROPERTY), ChangeModel(typeName=age, typeValue=null, typeComment=年龄, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=stature, typeValue=null, typeComment=身高, changeType=UNCHANGED, before=0.0, after=0.0, children=null, modelType=PROPERTY), ChangeModel(typeName=weight, typeValue=null, typeComment=体重, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=gender, typeValue=null, typeComment=性别, changeType=UNCHANGED, before=0, after=0, children=null, modelType=PROPERTY), ChangeModel(typeName=totalDeposit, typeValue=null, typeComment=总存款, changeType=UNCHANGED, before=null, after=null, children=null, modelType=PROPERTY), ChangeModel(typeName=spouse, typeValue=null, typeComment=配偶, changeType=UNCHANGED, before=null, after=null, children=null, modelType=ENTITY_PROPERTY), ChangeModel(typeName=children, typeValue=null, typeComment=孩子们, changeType=UNCHANGED, before=null, after=null, children=null, modelType=LIST_PROPERTY)], modelType=ENTITY)], modelType=LIST_PROPERTY)], modelType=ENTITY)]

代码地址

entity-compare-api

总结

项目开发过程中,用到的一个小工具,性能问题,有待优化。后续看看能不能再精简一下配置内容与命名。