Java对比对象修改前与修改后字段发生的变化

2023-09-22 14:49:40
  1.   开发过程中,我们通常会对系统操作人对系统的操作进行记录,记录操作前后某个字段的变化,如下图  

 2.   提供一个工具类,可以比较一个对象里面,源对象,与修改后的对象,有哪些字段发生了改变, 第一步 


/**
 * @author qiankun.hu
 * @version 1.0.0
 * @createTime 2023年09月20日 17:00:00
 * @Description TODO
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnInfo {

    /**
     * 字段code
     */
    String columnCode() default "";

    /**
     * 字段名称
     */
    String columnName() default "";

    /**
     * 字段类型
     */
    String columnType() default "";
}

工具类代码

import com.example.demo.config.ColumnInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;

import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.*;

/**
 * @author qiankun.hu
 * @version 1.0.0
 * @createTime 2023年09月20日 17:00:00
 * @Description TODO
 */
public class MyBeanUtils extends BeanUtils {

    /**
     * 比较两个实体属性值,返回一个map以有差异的属性名为key,value为一个list分别存obj1,obj2此属性名的值
     *
     * @param obj1      源值
     * @param obj2      最新值
     * @param ignoreArr 选择忽略比较的属性数组
     * @return 属性差异比较结果map
     * <p>
     * 后面考虑添加注解字段名和中文名称,已达到提示对应的中文信息
     */
    public static Map<String, List<Object>> compareFields(Object obj1, Object obj2, String[] ignoreArr) {
        try {
            Map<String, List<Object>> map = new HashMap<String, List<Object>>();
            List<String> ignoreList = null;
            if (ignoreArr != null && ignoreArr.length > 0) {
                // array转化为list
                ignoreList = Arrays.asList(ignoreArr);
            }
            if (obj1 == null) {
                Class clazz = obj2.getClass();
                // 获取object的属性描述
                PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz,
                        Object.class).getPropertyDescriptors();
                // 这里就是所有的属性了
                for (PropertyDescriptor pd : pds) {
                    // 属性名
                    String name = pd.getName();
                    // 如果当前属性选择忽略比较,跳到下一次循环
                    if (ignoreList != null && ignoreList.contains(name)) {
                        continue;
                    }
                    // Field field = clazz.getDeclaredField(name);
                    Field field;
                    try {
                        field = clazz.getDeclaredField(name);
                    } catch (NoSuchFieldException e) {
                        try {
                            //此处用于解决继承导致的getDeclaredField不能直接获取父类属性的问题
                            field = clazz.getSuperclass().getDeclaredField(name);
                        } catch (NoSuchFieldException ee) {
                            //此处用于解决继承导致的getDeclaredField不能直接获取父类属性的问题
                            field = clazz.getSuperclass().getSuperclass().getDeclaredField(name);
                        }
                    }
                    ColumnInfo columnInfo = field.getAnnotation(ColumnInfo.class);
                    if (columnInfo != null) {
                        String columName = columnInfo.columnName();
                        String columnCode = columnInfo.columnCode();
                        String columnType = columnInfo.columnType();
                        name = name + "_" + columName + "_" + columnCode + "_" + columnType;
                    } else {
                        continue;
                    }
                    // get方法
                    Method readMethod = pd.getReadMethod();
                    Object o2 = readMethod.invoke(obj2);
                    if (o2 != null) {
                        List<Object> list = new ArrayList<Object>();
                        list.add(null);
                        list.add(o2);
                        map.put(name, list);
                    }
                }
            } else {
                // 只有两个对象都是同一类型的才有可比性
                if (obj1.getClass() == obj2.getClass()) {
                    Class clazz = obj1.getClass();
                    // 获取object的属性描述
                    PropertyDescriptor[] pds = Introspector.getBeanInfo(clazz,
                            Object.class).getPropertyDescriptors();
                    // 这里就是所有的属性了
                    for (PropertyDescriptor pd : pds) {
                        // 属性名
                        String name = pd.getName();
                        // 如果当前属性选择忽略比较,跳到下一次循环
                        if (ignoreList != null && ignoreList.contains(name)) {
                            continue;
                        }
                        //Field field = clazz.getDeclaredField(name);
                        Field field;
                        try {
                            field = clazz.getDeclaredField(name);
                        } catch (NoSuchFieldException e) {
                            try {
                                //此处用于解决继承导致的getDeclaredField不能直接获取父类属性的问题
                                field = clazz.getSuperclass().getDeclaredField(name);
                            } catch (NoSuchFieldException ee) {
                                //此处用于解决继承导致的getDeclaredField不能直接获取父类属性的问题
                                field = clazz.getSuperclass().getSuperclass().getDeclaredField(name);
                            }

                        }
                        ColumnInfo columnInfo = field.getAnnotation(ColumnInfo.class);
                        if (columnInfo != null) {
                            String columName = columnInfo.columnName();
                            String columnCode = columnInfo.columnCode();
                            String columnType = columnInfo.columnType();
                            name = name + "_" + columName + "_" + columnCode + "_" + columnType;
                        } else {
                            continue;
                        }
                        // get方法
                        Method readMethod = pd.getReadMethod();
                        // 在obj1上调用get方法等同于获得obj1的属性值
                        Object o1 = readMethod.invoke(obj1);
                        // 在obj2上调用get方法等同于获得obj2的属性值
                        Object o2 = readMethod.invoke(obj2);
                        if (o1 instanceof Timestamp) {
                            o1 = new Date(((Timestamp) o1).getTime());
                        }
                        if (o2 instanceof Timestamp) {
                            o2 = new Date(((Timestamp) o2).getTime());
                        }
                        if (o1 == null && o2 == null) {
                            continue;
                        } else if (o1 == null && o2 != null) {
                            List<Object> list = new ArrayList<Object>();
                            list.add(o1);
                            list.add(o2);
                            if (o2 instanceof String && StringUtils.isBlank(String.valueOf(o2))) {
                                o2 = null;
                            }
                            if (o2 != null) {
                                map.put(name, list);
                            }
                            continue;
                        }
                        // 比较这两个值是否相等,不等就可以放入map了
                        if (!o1.equals(o2)) {
                            List<Object> list = new ArrayList<Object>();
                            list.add(o1);
                            list.add(o2);
                            if(o2!=null){
                                o1 = o1 instanceof String && StringUtils.isBlank(String.valueOf(o1)) ? "" : o1;
                                Object info = (o2 instanceof String && StringUtils.isBlank(String.valueOf(o2)) ? "" : o2);
                                o2 = o2 == null ? "" : info;
                                if(!o1.equals(o2)){
                                    map.put(name, list);
                                }
                            }

                        }
                    }
                }
            }
            return map;
        } catch (Exception e) {
            return null;
        }
    }
}

3  进行测试

import com.alibaba.fastjson.JSON;
import com.example.demo.config.ColumnInfo;
import com.example.demo.util.MyBeanUtils;
import java.util.*;


/**
 * @author qiankun.hu
 * @version 1.0.0
 * @createTime 2022年10月14日 13:35:00
 * @Description TODO
 */
public class StudentDto {

    @ColumnInfo(columnCode = "name", columnName = "名称")
    private String name;
    @ColumnInfo(columnCode = "name", columnName = "名称")
    private String age;
    @ColumnInfo(columnCode = "studentNum", columnName = "学号")
    private Integer studentNum;
    @ColumnInfo(columnCode = "classNum", columnName = "班级号")
    private String classNum;

    public static void main(String[] args) {
        //老数据
        StudentDto s1 = new  StudentDto();
        s1.setName("张三");
        s1.setAge("18");
        s1.setStudentNum(9001);
        s1.setClassNum("高一二班");

        //修改后的数据
        StudentDto s2 = new  StudentDto();
        s2.setName("张三");
        s2.setAge("19");
        s2.setStudentNum(9002);
        s2.setClassNum("高一三班");

        //忽略对比的字段
        String[] arr = new String[1];
        arr[0] = "studentNum";

        //对比哪些字段发生改变,K 是发生改变的字段  value 是字段前后的变化
        Map<String, List<Object>> stringListMap = MyBeanUtils.compareFields(s1, s2, null);

        System.out.println( JSON.toJSONString(stringListMap) );
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public Integer getStudentNum() {
        return studentNum;
    }

    public void setStudentNum(Integer studentNum) {
        this.studentNum = studentNum;
    }

    public String getClassNum() {
        return classNum;
    }

    public void setClassNum(String classNum) {
        this.classNum = classNum;
    }
}

输出结果如下:
{
    "age_名称_name_": ["18", "19"],
    "studentNum_学号_studentNum_": [9001, 9002],
    "classNum_班级号_classNum_": ["高一二班", "高一三班"]
}

我们可以看到,对象修改后与修改后,相关字段产生的变化,如果有不需要对比的字段,传入相同的参数既可

更多推荐

轻松搞定Spring集成缓存,让你的应用程序飞起来!

Spring集成缓存缓存接口开启注解缓存注解使用@Cacheable@CachePut@CacheEvict@Caching@CacheConfig缓存存储使用ConcurrentHashMap作为缓存使用Ehcache作为缓存使用Caffeine作为缓存主页传送门:📀传送Spring提供了对缓存的支持,允许你将数据

网络安全(黑客)自学

前言:想自学网络安全(黑客技术)首先你得了解什么是网络安全!什么是黑客网络安全可以基于攻击和防御视角来分类,我们经常听到的“红队”、“渗透测试”等就是研究攻击技术,而“蓝队”、“安全运营”、“安全运维”则研究防御技术。无论网络、Web、移动、桌面、云等哪个领域,都有攻与防两面性,例如Web安全技术,既有Web渗透,也有

Palantir的“英伟达时刻”即将到来

来源:猛兽财经作者:猛兽财经总结(1)由于投资者对生成式人工智能的兴趣持续增加,Palantir的股价一直在上涨。(2)Palantir已经连续三个季度实现了GAAP盈利,并将很快有资格被纳入标普500指数。(3)Palantir拥有非常健康的资产负债表,并授权了一项股票回购计划。(4)虽然市场已经消化了很多乐观情绪,

RocketMQ高性能核心原理与源码架构剖析

文章目录1、源码环境搭建1.1、主要功能模块1.2、源码启动服务1.2.1、启动nameServer1.2.2、启动Broker1.2.3、发送消息1.2.4、消费消息1、源码环境搭建1.1、主要功能模块​RocketMQ的官方Git仓库地址:https://github.com/apache/rocketmq可以用g

【数据结构初阶】三、 线性表里的链表(无头+单向+非循环链表)

=========================================================================相关代码gitee自取:C语言学习日记:加油努力(gitee.com)====================================================

读高性能MySQL(第4版)笔记10_查询性能优化(上)

1.三管齐下1.1.不做、少做、快速地做1.2.如果查询太大,服务端会拒绝接收更多的数据并抛出相应错误1.3.如果查询写得很糟糕,即使库表结构再合理、索引再合适,也无法实现高性能1.4.查询优化、索引优化、库表结构优化需要齐头并进,一个不落1.5.PerconaToolkit中的pt-archiver工具2.响应时间2

【vivo秋招0912】三、最少开发工时总和 <模拟>

三、最少开发工时总和某开发小组近期承接了多个研发项目,作为组长的你需要为员工分配工作任务。具体要求如下:项目划分到的任务工时用二维数组tasks表示,其中tasks[i][j]表示的是第i个项目中第j个任务的开发工时;现在组内员工有n个,每个工作任务只能分配给一位员工,一位员工可以被分配多个任务,一个任务完成才能进行下

【用unity实现100个游戏之12】unity制作一个俯视角2DRPG《类星露谷物语》资源收集游戏demo

文章目录前言加快编辑器运行速度素材(1)场景人物(2)工具一、人物移动和动画切换二、走路灰尘粒子效果探究实现三、树木排序设计方法一方法二四、绘制拿工具的角色动画五、砍树实现六、存储拾取物品引入Unity的可序列化字典类七、实现靠近收获物品自动吸附八、树木被砍掉的粒子效果九、新增更多可收集物十、更多工具切换十一、扩展源码

第五天:基于gec6818开发板或stm32驱动对RFID高频读卡器进行防碰撞获取卡号,读取,写入内容操作

在ARM板上通过防碰撞指令获取高频RFID卡的卡号高频RFID一般频率在13.56MHz左右,识别距离可扩展至1.5米,具有防碰撞特性,可以同时读取多个电子标签。标签可以存储少量数据,多用于需要存储数据、近距离的识别应用,如:图书馆管理系统、场地通道安全管理系统等。mifare522模块的数据帧格式gec6818开发板

[C++ 网络协议] I/O流分离所带来的半关闭问题

1.问题和解决方法根据所学内容,I/O流分离现如今有如下2种方法:1.调用进程fork函数,分离出子进程,主进程和子进程分别进行输入流的读和输出流的写。2.用FILE指针按读模式和写模式将输入流和输出流进行区分。第一种方法,可以对输出流调用shutdown函数进行半关闭,从而不会影响到输入流接收客户端的数据,这是没问题

从理解js双重递归执行顺序到用递归方式实现二叉树中序遍历

今天在学习力扣上94题二叉树的中序遍历时,js的实现方法之一是递归,但是函数内递归是双重,花了一些时间来理解双重递归调用的执行顺序。先看如下例子,参考文章(双递归的执行过程理解)示例代码如下:constfn=(n)=>{if(n>0){console.log('n1====',n)fn(n-1)console.log(

热文推荐