[实践总结] Java 获取对象被更新的属性和值

诉求

更新User后,收集哪些字段的哪些值被更新了


思路

比较originUsernewUser,收集值不同的属性以及变化前后的值。


实现方式1

    /**
     * <p>该方法用于比较两个对象的属性,并返回一个映射,表示被更新了的属性及其原值和新值。此方法适用于具有相同类类型的对象,
     * 并通过Java反射机制获取并比较它们的所有非合成字段(不包括serialVersionUID)的值。</p>
     *
     * @param originObject 原始对象实例,用于获取原有属性值
     * @param newObject    新对象实例,用于获取更新后的属性值
     * @return 一个Map<String, Map < String, Object>>类型的对象,其中:
     * - 键(String类型):表示发生变化的属性名称
     * - 值(Map<String, Object>类型):包含键为"origin"和"new"的子映射,分别表示原值和新值
     * @throws IntrospectionException    如果在获取属性描述符时出现异常
     * @throws IllegalAccessException    如果在访问或调用getter方法时无权访问私有属性
     * @throws InvocationTargetException 如果在调用getter方法时抛出了异常
     */
    public static Map<String, Map<String, Object>> getChange(Object originObject, Object newObject) {
        // 存在为null比较就没意义了
        if (anyNull(originObject, newObject)) {
            return new HashMap<>();
        }

        // 不同类型无法比较
        if (ObjectUtils.notEqual(originObject.getClass(), newObject.getClass())) {
            return new HashMap<>();
        }

        // 内容都相等所以没必要比较
        if (Objects.deepEquals(originObject, newObject)) {
            return new HashMap<>();
        }

        Map<String, Map<String, Object>> result = Maps.newHashMap();

        // 反射比较对象的字段
        Field[] declaredFields = originObject.getClass().getDeclaredFields();
        for (Field field : declaredFields) {
            // 检查Field是否为合成字段、如果该字段是合成字段则不进行比较
            if (field.isSynthetic()) {
                continue;
            }

            // serialVersionUID适用于java序列化机制,非业务属性
            if ("serialVersionUID".equals(field.getName())) {
                continue;
            }

            // 实现方式1
            // field.setAccessible(true);
            // Object o = field.get(originObject);
            // Object n = field.get(originObject);

            try {
                // 实现方式2
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), originObject.getClass());
                Method readMethod = propertyDescriptor.getReadMethod();
                Object originValue = readMethod.invoke(originObject);
                Object newValue = readMethod.invoke(newObject);
                if (!Objects.deepEquals(originValue, newValue)) {
                    // 值不相等的话记录属性和值
                    HashMap<String, Object> keyValue = Maps.newHashMap();
                    keyValue.put("origin", originValue);
                    keyValue.put("new", newValue);
                    result.put(field.getName(), keyValue);
                }
            } catch (IntrospectionException | IllegalAccessException | InvocationTargetException e) {
                log.error("getChange occur error" + e.getMessage());
            }
        }
        return result;
    }

实现方式2

    /**
     * <p>该静态方法用于获取两个相同类型对象之间的属性变化信息,比较前后对象(`prev`和`after`)的属性值,并返回一个映射表,
     * 映射表的键为发生变化的属性名,值为一个包含原值和新值的列表。</p>
     *
     * <p>通过Java反射API遍历对象的所有属性(排除预定义的排除字段如"id", "created_at", "updated_at"以及"serialVersionUID"),
     * 比较每个属性在前后对象中的取值是否相等。如果存在差异,则将此属性及其对应的原始值和更新后的值加入结果集合中。</p>
     *
     * @param <T>   泛型参数,表示被比较的对象类型
     * @param prev  需要进行比较的原始对象实例
     * @param after 需要进行比较的新对象实例
     * @return 一个Map对象,键为字符串类型的属性名,值为一个Object列表,列表中包含了旧值和新值
     * @throws RuntimeException 如果在使用Java反射过程中发生异常,例如IntrospectionException或在读取属性时抛出IllegalAccessException、InvocationTargetException
     */
    public static <T> Map<String, List<Object>> getChange2(T prev, T after) {
        Set<String> exceptFields = new HashSet<>(Arrays.asList("id", "created_at", "updated_at"));
        Map<String, List<Object>> res = new HashMap<>();
        Class<?> aClass = prev.getClass();
        PropertyDescriptor[] properties = new PropertyDescriptor[0];
        try {
            properties = Introspector.getBeanInfo(aClass, Object.class).getPropertyDescriptors();
        } catch (IntrospectionException e) {
            System.out.println("Introspector.getBeanInfo exception" + e.getMessage());
        }

        for (PropertyDescriptor propertyDescriptor : properties) {
            String propertyName = propertyDescriptor.getName();
            if ("serialVersionUID".equals(propertyName)) {
                continue;
            }
            if (exceptFields.contains(propertyName)) {
                continue;
            }
            Method readMethod = propertyDescriptor.getReadMethod();
            try {
                Object preValue = readMethod.invoke(prev);
                Object afterValue = readMethod.invoke(after);
                if (allNull(preValue, afterValue)) {
                    continue;
                }
                if (anyNull(preValue, afterValue)) {
                    res.put(propertyName, Arrays.asList(preValue, afterValue));
                    continue;
                }
                if (!preValue.equals(afterValue)) {
                    res.put(propertyName, Arrays.asList(preValue, afterValue));
                }
            } catch (IllegalAccessException | InvocationTargetException e) {
                System.out.println("data compare occur error" + e.getMessage());
            }
        }
        return res;
    }

测试验证

public class MethodUtilsTest {

    @Test
    public void getChangeNoChange() {
        User user1 = new User();
        user1.setName("user2");
        user1.setAge("19");
        user1.setNum("123456");

        User user2 = new User();
        user2.setName("user2");
        user2.setAge("19");
        user2.setNum("123456");

        Map<String, Map<String, Object>> change = MethodUtils.getChange(user1, user2);
        Assert.assertEquals("{}", change.toString());
    }

    @Test
    public void getChangeWithChange() {
        User user1 = new User();
        user1.setName("user1");
        user1.setAge("19");
        user1.setNum("123456");

        User user2 = new User();
        user2.setName("user2");
        user2.setAge("18");
        user2.setNum("123457");

        Map<String, Map<String, Object>> change = MethodUtils.getChange(user1, user2);
        Assert.assertEquals("{num={new=123457, origin=123456}, name={new=user2, origin=user1}, age={new=18, origin=19}}", change.toString());
    }

    @Data
    static class User {
        private String name;
        private String age;
        private String num;
    }
}

参考

对象被更新后、获取更新了的属性和值

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值