数据比较器,对比数据前后变化细节
- 前言
- 设计
- 正文
- 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、定义注解
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(