Java对比两个对象的不同,可对比基本类型、对象套对象、List

Java对比两个对象的不同


JDK版本:1.8 (由于用到了lambda、stream)
Maven


<dependency>
	<groupId>cn.hutoolgroupId>
	<artifactId>hutool-allartifactId>
	<version>4.5.11version>
dependency>

<dependency>
	<groupId>org.projectlombokgroupId>
	<artifactId>lombokartifactId>
	<version>1.18.8version>
dependency>

假设我们现在有个需求,信息变更审核功能,要求要记录信息变更 修改前修改后 的值。
eg: 姓名:由 张三 变更为 李四

核心注解

import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnName {

    /**
     * 字段名称
     * @return
     */
    String value() default "";

    /**
     * 当前字段的类型,如果是集合指定集合中的类型 
     * 基本类型 可不指定
     * 用于复杂类型 eg: Object | List
     * @return
     */
    Class<?> classType() default void.class;

    /**
     * 是否是集合类型
     * 如果是集合类型必须指定其
     * 1. 集合中存放的对象类型 {@link ColumnName#classType()}
     * 2. 集合中的对象的唯一标识 {@link ColumnName#onlyMark()}
     * @return
     */
    boolean isList() default false;


    /**
     * 如果是集合类型还要知道该集合对象中什么字段可以确定唯一 可多个 逗号隔开 eg: id,name
     * @return
     */
    String onlyMark() default "";

    /**
     * 如果是日期类型 可自定义日期格式
     * 默认 yyyy-MM-dd HH:mm:ss
     * 常用格式,包括:
     *      HH:mm:ss
     *      yyyy-MM-dd
     *      yyyy-MM-dd HH:mm
     *      yyyy-MM-dd HH:mm:ss
     *      yyyy-MM-dd HH:mm:ss.SSS
     * @return
     */
    String dateFormat() default "yyyy-MM-dd HH:mm:ss";
}
 
  

有了以上注解后我们来看我们要对比的实体类
假设我们现在有一个人的类
里面包含 编号、姓名、年龄、生日,以及这个人养的一条狗,以及一个女朋友的List集合

人类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {

    @ColumnName("编号")
    private Integer id;

    @ColumnName("姓名")
    private String name;

    @ColumnName("年龄")
    private Integer age;

    @ColumnName(value = "生日", dateFormat = "yyyy-MM-dd")
    private Date birthDay;

    @ColumnName(value = "狗", classType = Dog.class)
    private Dog dog;

    @ColumnName(value = "女朋友", isList= true, classType = GirlFriend.class, onlyMark = "id,age")
    private List<GirlFriend> girlFriends;
    
}

狗类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {

    @ColumnName("狗名字")
    private String name;

}

女朋友类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class GirlFriend {

    @ColumnName("编号")
    private Integer id;

    @ColumnName("姓名")
    private String name;

    @ColumnName("年龄")
    private Integer age;

}

创建一个存放不同的类,又来存放最终对比出来的不同的结果

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Different {

    // 字段
    private String field;

    // 对应的字段提示 eg: 姓名
    private String fieldName;

    // 变更前的值
    private String oldData;

    // 变更后的值
    private String newData;

    @Override
    public String toString() {
        return "数据库字段:'" + field + "', 字段:'" + fieldName + '\'' + ", 老值:'" + oldData + '\'' + ", 新值:'" + newData + '\'';
    }
    
}

现在我们有一个修改之前的人对象和修改之后的人对象
要求:获取到两者的不同之处

public static void main(String[] args) throws Exception {
    // 修改之前的对象
    Person person1 = new Person();
    person1.setId(1);
    person1.setName("张三");
    person1.setBirthDay(DateUtil.parse("2018-05-05"));

    person1.setAge(18);

    Dog dog = new Dog();
    dog.setName("张三的狗");
    person1.setDog(dog);

    person1.setGirlFriends(Arrays.asList(
            new GirlFriend(1, "小花", 19)
            , new GirlFriend(2, "小丽", 20)
            , new GirlFriend(3, "小青", 10)
    ));
    
	// 修改之后的对象
    Person person2 = new Person();
    person2.setId(1);
    person2.setName("张三");
    person2.setBirthDay(DateUtil.parse("2018-05-05"));
    person2.setAge(18);

    Dog dog2 = new Dog();
    dog2.setName("张三的狗");
    person2.setDog(dog2);

    person2.setGirlFriends(Arrays.asList(
            new GirlFriend(1, "小花", 19)
            , new GirlFriend(2, "小丽", 20)
            , new GirlFriend(3, "小青", 10)
    ));

    // 获取两个对象的不同
    getDifferents(person1, person2, Person.class).forEach(System.out::println);
}

我们修改 person2 的name 修改为: 张三三 并执行 Main
可以得到以下结果
Java对比两个对象的不同,可对比基本类型、对象套对象、List_第1张图片
继续修改 person2 的birthday 修改为 :2018-10-10 并执行 Main
可以得到以下结果
Java对比两个对象的不同,可对比基本类型、对象套对象、List_第2张图片
如果日期的格式不符合预期结果 可以在@ColumnName#dateFormat() 指定其日期的格式
继续修改dog2的name 为:张三的大黑狗
可以得到以下结果
Java对比两个对象的不同,可对比基本类型、对象套对象、List_第3张图片
复杂的类型需要在实体类中指定类型 eg:@ColumnName#classType()
在这里插入图片描述
我们继续修改女朋友的集合中的数据
将名字叫小花的女朋友名字修改为 花姑娘 结果
Java对比两个对象的不同,可对比基本类型、对象套对象、List_第4张图片
由于集合会出现 添加、修改、删除三种 不同的情况
所有这里再添加一个接口

public interface FieldNameHandler {

    /**
     * 增
     */
    String ADDED_PREFIX = "addedPrefix";

    /**
     * 删
     */
    String DELETE_PREFIX = "deletePrefix";

    /**
     * 改
     */
    String UPDATE_PREFIX = "updatePrefix";

    /**
     * 处理添加字段名
     * @return 添加数据时的字段名前缀
     */
    String addedPrefix();

    /**
     * 处理删除字段名
     * @return 删除数据时的字段名前缀
     */
    String deletePrefix();

    /**
     * 处理修改字段名
     * @return 修改数据时的字段名前缀
     */
    String updatePrefix();

}

让我们的GirlFriend 实现 FieldNameHandler
实现后我们的GirlFriend变成

@Data
@NoArgsConstructor
@AllArgsConstructor
public class GirlFriend implements FieldNameHandler {

    @ColumnName("编号")
    private Integer id;

    @ColumnName("姓名")
    private String name;

    @ColumnName("年龄")
    private Integer age;

    @Override
    public String addedPrefix() {
        return StrUtil.format("添加女朋友 ");
    }

    @Override
    public String deletePrefix() {
        return StrUtil.format("删除女朋友 ");
    }

    /**
     * 复杂类型的 变更字段 可以自定义
     * @return 返回值会加上 {@link ColumnName#value()} 的值
     * 如果当前对象的姓名改了 字段的值就为 : 修改编号为:1的女朋友
     */
    @Override
    public String updatePrefix() {
        return StrUtil.format("修改编号为:" + this.id + "的女朋友 ");
    }
}

我们再次运行Main
Java对比两个对象的不同,可对比基本类型、对象套对象、List_第5张图片
字段本来的值是@ColumnName#value() 的值 姓名 实现了FieldNameHandler接口后如果发现是新增、删除、修改
会自动调用 实现类中的addedPrefix、deletePrefix、updatePrefix 并 加上@ColumnName#value()

添加删除女朋友这里就不模拟了
最后贴上getDifferents 写的比较粗糙 见谅

/**
* 数据修改对比统计
 *
 * @param oldT      修改前
 * @param newT      修改后
 * @param className 类名
 * @param 
 * @return
 * @throws Exception
 */
public static <T> List<Different> getDifferents(T oldT, T newT, Class<?> className) throws IllegalAccessException, InstantiationException {

    // 存放处理结果
    List<Different> differents = new ArrayList<>();

    // 获取当前类的所有字段
    Field[] fields = className.getDeclaredFields();

    for (Field f : fields) {
        // 过滤 static、 final、private static final字段
        if (f.getModifiers() == Modifier.FINAL || f.getModifiers() == Modifier.STATIC || f.getModifiers() == (Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL)) {
            continue;
        }

        // 获取当前字段注解
        ColumnName annotationColumn = f.getAnnotation(ColumnName.class);
        if (annotationColumn == null) {
            continue;
        }

        // 反射获取当前老对象的字段值
        Object oldV = ReflectUtil.getFieldValue(oldT, f.getName());
        // 反射获取当前新对象的字段值
        Object newV = ReflectUtil.getFieldValue(newT, f.getName());

        // 当前字段的类型 用于复杂类型 eg: Object List Set
        final Class<?> dataType = annotationColumn.classType();
        final String typeName = annotationColumn.classType().getTypeName();

        // 如果他是集合类型
        if (annotationColumn.isList()) {
            // 二次校验是否是集合类型
            if (Objects.isNull(oldV)) oldV = new ArrayList<>();
            if (Objects.isNull(newV)) newV = new ArrayList<>();
            final boolean isCollection = Collection.class.isAssignableFrom(oldV.getClass());
            if (isCollection) {
                final List oldList = (List) oldV;
                final List newList = (List) newV;
                // 两个集合都是空的继续处理剩余字段
                if (CollectionUtil.isEmpty(oldList) && CollectionUtil.isEmpty(newList)) {
                    continue;
                }

                // 老的是空的 新的有值 说明是集合中新添加了值 并不存在修改操作
                if (CollectionUtil.isEmpty(oldList) && CollectionUtil.isNotEmpty(newList)) {
                    for (Object n : newList) {
                        differents.addAll(getDifferents(n.getClass().newInstance(), n, dataType));
                    }
                    continue;
                }

                // 新的是空的 老的有值 说明是集合中的值全部删除了
                if (CollectionUtil.isNotEmpty(oldList) && CollectionUtil.isEmpty(newList)) {
                    for (Object o : oldList) {
                        differents.addAll(getDifferents(o, o.getClass().newInstance(), dataType));
                    }
                    continue;
                }
                // 创建临时集合
                List allList = new ArrayList();
                allList.addAll(oldList);
                allList.addAll(newList);

                for (Object o : oldList) {
                    int matchCount = 0;
                    for (Object n : newList) {
                        final String key = annotationColumn.onlyMark();
                        // 如果存在多个唯一标识字段特殊处理
                        if (key.contains(",")) {
                            final String[] keys = key.split(",");
                            boolean isSuccess = true;
                            for (String s : keys) {
                                final Object o1 = ReflectUtil.getFieldValue(o, s);
                                final Object n1 = ReflectUtil.getFieldValue(n, s);

                                // 有一个不匹配跳出循环
                                if (!o1.equals(n1)) {
                                    isSuccess = false;
                                    break;
                                }
                            }
                            if (isSuccess) {
                                // 匹配到就--
                                matchCount--;
                                differents.addAll(getDifferents(o, n, dataType));
                            } else {
                                // 没有匹配到就++
                                matchCount++;
                                // 如果不是一个对象 就去判断该新对象是否是之前已经判断过了的对象
                                final long count = allList.stream().filter(e -> {
                                    for (String s : keys) {
                                        final Object o1 = ReflectUtil.getFieldValue(e, s);
                                        final Object n1 = ReflectUtil.getFieldValue(n, s);

                                        // 有一个不匹配跳出循环
                                        if (!o1.equals(n1)) {
                                            return false;
                                        }
                                    }
                                    return true;
                                }).count();
                                if (count < 2) {
                                    // 说明是新添加了一个
                                    differents.addAll(getDifferents(o.getClass().newInstance(), n, dataType));
                                    allList.add(n);
                                }
                            }

                        } else {
                            final Object o1 = ReflectUtil.getFieldValue(o, key);
                            final Object n1 = ReflectUtil.getFieldValue(n, key);
                            if (o1.equals(n1)) {
                                // 匹配到就--
                                matchCount--;
                                differents.addAll(getDifferents(o, n, dataType));
                            } else {
                                // 没有匹配到就++
                                matchCount++;
                                // 如果不是一个对象 就去判断该新对象是否是之前已经判断过了的对象
                                final long count = allList.stream().filter(e -> ReflectUtil.getFieldValue(e, key).equals(ReflectUtil.getFieldValue(n, key))).count();
                                if (count < 2) {
                                    // 说明是新添加了一个
                                    differents.addAll(getDifferents(o.getClass().newInstance(), n, dataType));
                                    allList.add(n);
                                }

                            }
                        }

                    }
                    // 没配到说明是老数据本来有新的数据删除了
                    if (matchCount == newList.size()) {
                        differents.addAll(getDifferents(o, o.getClass().newInstance(), dataType));
                    }

                }

            }
        }

        // 不能是 void 和 基本类型
        if (!"void".equals(typeName) && !ClassUtil.isBasicType(dataType)) {
            final Class<?> aClass = annotationColumn.classType();
            differents.addAll(getDifferents(oldV, newV, aClass));
            continue;
        }

        // 检查新老对象的值是否一致 不一致记录
        if (!Objects.equals(newV, oldV)) {

            // 字段名字
            String fieldName = StrUtil.isNotEmpty(annotationColumn.value()) ? annotationColumn.value() : f.getName();

            // 获取当前类所实现的所有接口
            final Class<?>[] interfaces = className.getInterfaces();

            // 是否实现了FieldNameHandler接口,如果实现了FieldNameHandler接口 就追加调用 FieldNameHandler#handle()
            final long count = Stream.of(interfaces).filter(a -> a.equals(FieldNameHandler.class)).count();
            if (count > 0) {
                Object invoke = null;
                // 添加了数据
                if (oldT.getClass().newInstance().equals(oldT)) {
                    invoke = ReflectUtil.invoke(oldT, FieldNameHandler.ADDED_PREFIX);
                }
                // 删除了数据
                else if (newT.getClass().newInstance().equals(newT)) {
                    invoke = ReflectUtil.invoke(oldT, FieldNameHandler.DELETE_PREFIX);
                }
                // 修改了数据
                else {
                    invoke = ReflectUtil.invoke(oldT, FieldNameHandler.UPDATE_PREFIX);
                }
                if (Objects.nonNull(invoke)) {
                    fieldName = invoke + fieldName;
                }

            }

            // 老对象值
            String oldValue = null != oldV ? oldV.toString() : "";
            // 新对象值
            String newValue = null != newV ? newV.toString() : "";

            // 日期格式特殊处理
            if (oldV instanceof Date && newV instanceof Date) {
                if (StrUtil.isNotEmpty(oldValue)) {
                    oldValue = DateUtil.format((Date) oldV, annotationColumn.dateFormat());
                }
                if (StrUtil.isNotEmpty(newValue)) {
                    newValue = DateUtil.format((Date) newV, annotationColumn.dateFormat());
                }
            }

            // 添加处理结果中
            differents.add(new Different(f.getName(), fieldName, oldValue, newValue));
        }
    }
    return differents;
}

转载注明出处 谢谢。

你可能感兴趣的:(Java对比两个对象的不同,可对比基本类型、对象套对象、List)