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

前言

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

设计

  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;

/**
 * 功能描述: 属性实体标识 <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
     
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值