一个简单的实现对象对比工具

一个简单的实现对象对比工具

通过注解+反射实现同类对象对比,提取出不同值的属性

前期准备

import lombok.Data;
import java.util.List;

@Data
public class User {

    /**
     * 用户id
     */
    private Long id;

    /**
     * 性别
     */
    @Compare
    private String sex;

    /**
     * 姓名
     */
    @Compare
    private String name;

    /**
     * 地址
     */
    @Compare
    private String address;

    /**
     * 角色列表
     */
    @Compare(itemType = Role.class)
    private List<Role> roleList;
}
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class Role {

    /**
     * 角色id
     */
    private Long id;

    /**
     * 权限
     */
    @Compare
    private String permission;

    /**
     * 状态
     */
    @Compare
    private String status;

    /**
     * 访问级别
     */
    @Compare
    private String accessLevel;
}
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import java.text.SimpleDateFormat;

/**
 * 使用双重校验锁实现单例模式
 *      解决点:
 *          一个全局使用的类频繁的创建和笑给,从而提升整体的性能
 *
 */
public class JsonUtils {

    private static volatile ObjectMapper mapper;

    private JsonUtils(){

    }

    public static final ObjectMapper getMapper(){
        if(mapper == null){
            synchronized (JsonUtils.class){
                if (mapper == null) {
                    mapper = new ObjectMapper();
                    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false);

                    //在序列化时日期格式默认为 yyyy-MM-dd'T'HH:mm:ss.SSSZ
                    mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);//在序列化时自定义时间日期格式
                    mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

                    // 过滤掉null值的属性
                    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
                }
            }
        }
        return mapper;
    }
}

核心代码

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface Compare {
    Class<?> itemType() default Object.class; // 默认类型为Object
}
import java.lang.reflect.Field;
import java.util.*;

public class DiffUtil {
    public static Map<String, Object> findDifferences(Object original, Object updated) throws IllegalAccessException, NoSuchFieldException {
        Map<String, Object> differences = new HashMap<>();

        // 空指针检查
        if (original == null || updated == null) {
            throw new IllegalArgumentException("Original and updated objects must not be null");
        }

        Class<?> originalClass = original.getClass();
        Class<?> updatedClass = updated.getClass();

        if (!originalClass.equals(updatedClass)) {
            throw new IllegalArgumentException("Original and updated objects must be of the same class");
        }

        for (Field field : originalClass.getDeclaredFields()) {
            field.setAccessible(true); // 允许访问私有字段
            if (field.isAnnotationPresent(Compare.class)) {
                Object originalValue = field.get(original);
                Object updatedValue = field.get(updated);

                if (!equalsSafe(originalValue, updatedValue)) {
                    differences.put(field.getName(), Map.of("ov", toSafeString(originalValue), "nv", toSafeString(updatedValue)));
                }

                // 处理列表字段
                if (List.class.isAssignableFrom(field.getType())) {
                    compareLists(originalValue, updatedValue, differences, field.getAnnotation(Compare.class).itemType());
                }
            }
        }

        return differences;
    }

    private static void compareLists(Object originalList, Object updatedList, Map<String, Object> differences, Class<?> itemType) throws IllegalAccessException, NoSuchFieldException {
        if (originalList == null && updatedList == null) return;

        List<?> originalItems = (List<?>) originalList;
        List<?> updatedItems = (List<?>) updatedList;

        List<Map<String, Object>> addedItems = new ArrayList<>();
        List<Map<String, Object>> updatedItemsList = new ArrayList<>();

        Map<Long, Object> originalItemMap = new HashMap<>();
        if(Objects.nonNull(originalItems)){
            for (Object originalItem : originalItems) {
                Field idField = itemType.getDeclaredField("id");
                idField.setAccessible(true); // 确保可以访问私有字段
                Long id = (Long) idField.get(originalItem);
                originalItemMap.put(id, originalItem);
            }
        }


        if(Objects.nonNull(updatedItems)){
            for (Object newItem : updatedItems) {
                Field idField = itemType.getDeclaredField("id");
                idField.setAccessible(true); // 确保可以访问私有字段
                Long newId = (Long) idField.get(newItem);
                Object originalItem = originalItemMap.get(newId);

                if (originalItem == null) {
                    // 新增的项
                    Map<String, Object> itemData = new HashMap<>();
                    itemData.put("id", newId); // 添加 id 字段
                    for (Field itemField : itemType.getDeclaredFields()) {
                        itemField.setAccessible(true); // 确保可以访问私有字段
                        if (itemField.isAnnotationPresent(Compare.class)) {
                            itemData.put(itemField.getName(), toSafeString(itemField.get(newItem)));
                        }
                    }
                    addedItems.add(itemData);
                } else {
                    // 更新的项
                    for (Field itemField : itemType.getDeclaredFields()) {
                        if (itemField.isAnnotationPresent(Compare.class)) {
                            itemField.setAccessible(true); // 确保可以访问私有字段
                            Object originalValue = itemField.get(originalItem);
                            Object newValue = itemField.get(newItem);
                            if (!equalsSafe(originalValue, newValue)) {
                                updatedItemsList.add(Map.of("id", newId, itemField.getName(), Map.of(
                                        "ov", toSafeString(originalValue),
                                        "nv", toSafeString(newValue)
                                )));
                            }
                        }
                    }
                }
            }
        }

        if (!addedItems.isEmpty() || !updatedItemsList.isEmpty()) {
            differences.put(itemType.getSimpleName().toLowerCase() + "List", Map.of("add", addedItems, "update", updatedItemsList));
        }
    }

    private static boolean equalsSafe(Object a, Object b) {
        return toSafeString(a).equals(toSafeString(b));
    }

    private static String toSafeString(Object obj) {
        return obj == null ? "" : obj.toString();
    }
}

测试代码

@Test
    public void objectDiffTest() throws JsonProcessingException, NoSuchFieldException, IllegalAccessException {
        // 第一个User对象
        User originalUser = new User();
        originalUser.setId(1L);
        originalUser.setSex("男");
        originalUser.setName("张三");
        originalUser.setAddress("上海");
        originalUser.setRoleList(Arrays.asList(
                new Role(1L, "管理员", "停用", "高"),
                new Role(2L, "编辑", "启用", "中")
        ));

        // 第二个User对象
        User updateUser = new User();
        updateUser.setId(1L); // id保持一致
        updateUser.setSex("女");
        updateUser.setName("李四");
        updateUser.setAddress("上海");
        updateUser.setRoleList(Arrays.asList(
                new Role(3L, "用户", "启用", "低"),
                new Role(1L, "管理员", "启用", "高"),
                new Role(2L, "编辑", "启用", "中")
        ));

        Map<String, Object> differences = DiffUtil.findDifferences(originalUser, updateUser);
        System.out.println(JsonUtils.getMapper().writeValueAsString(differences));
    }

返回结果

{
    "sex": {
        "nv": "女",  // nv代表新值
        "ov": "男"	// ov代表原始值
    },
    "name": {
        "nv": "李四",
        "ov": "张三"
    },
    "roleList": {
        "update": [
            {
                "id": 1,
                "status": {
                    "nv": "启用",
                    "ov": "停用"
                }
            }
        ],
        "add": [
            {
                "accessLevel": "低",
                "permission": "用户",
                "id": 3,
                "status": "启用"
            }
        ]
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值