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

  • 前言
  • 设计
  • 正文
    • 1、定义注解
      • 1) 实体注解,确定实体名称
      • 2) 主键注解,校验数据是否一致
      • 3) 属性描述注解
      • 4) 顺序注解
      • 5) 排除注解,不进行比较
      • 6) 自定义比较器
    • 2、自定义比较器
      • 1) 比较器接口
      • 2) 默认比较器实现
    • 3、异常类
    • 4、枚举定义
      • 1) 变化类型:新增,修改,删除,无变化等四种情况
      • 2) 模型类型枚举
      • 3) 异常枚举
      • 4) 实体类型枚举
    • 5、处理器,与实体类型枚举一起使用
      • 1) 处理器接口
      • 2) 处理器抽象类
      • 3) 基本类型处理器
      • 4) 实体类型处理器
      • 5) List处理器
      • 5) Map处理器
    • 6、模型定义
      • 1) 实体解析模型
      • 2) 变化模型
    • 7、解析工具 AnalyzeUtil
    • 8、数据比较核心类
    • 9、提供对外调用类 CompareCore
  • 测试
  • 结果
  • 代码地址
  • 总结

前言

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

设计

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

总体结构如下:

正文

1、定义注解

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

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

import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * 功能描述: 属性实体标识 
*/
@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;/** * 功能描述: 唯一标记,可以又多个,用于联合索引 
*/
@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;/** * 功能描述: 属性描述 
*/
@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;/** * 功能描述: 属性排序 
*/
@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;/** * 功能描述: 属性忽略比较 
*/
@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;/** * 功能描述: 属性比较器,可以自定义 
*/
@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD, ElementType.FIELD})public @interface PropertyComparator { /** 比较器,默认比较器 */Class<" />extends IComparator> compare() default DefaultComparator.class;}

2、自定义比较器

1) 比较器接口

/** * 功能描述: 比较器接口 
*/
public interface IComparator<T> { /** * 功能描述: 对象比较
* * @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;/** * 功能描述: 默认比较器 
*/
public class DefaultComparator implements IComparator<Object> { @Overridepublic 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;/** * 功能描述: 比较异常类 
*/
@AllArgsConstructorpublic 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;/** * 功能描述: 变化类型枚举 
*/
@Getter@AllArgsConstructorpublic 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;/** * 功能描述: 模型类型 
*/
@Getter@AllArgsConstructorpublic 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;/** * 功能描述: 异常枚举 
*/
@Getter@AllArgsConstructorpublic 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;/** * 功能描述: 实体类型枚举 
*/
@Getter@AllArgsConstructorpublic 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;/** * 功能描述: 实体过程 
*/
public interface IPropertyProcess { /** * 功能描述: 单个对象处理
* * @param pm 属性模型 * @param cte 变化类型 * @return "com.cah.project.compare.model.ChangeModel" */
ChangeModel process(PropertyModel pm, ChangeTypeEnum cte) throws InstantiationException, IllegalAccessException;/** * 功能描述: 两个对象处理
* * @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();/** * 功能描述: 获取基本的类型 
* * @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(