对比相同类对象字段的变化(可用于审计或者轨迹)

简介: 对比相同类对象字段的变化(可用于审计或者轨迹)

背景

有时候我们需要做一下审计日志或者操作记录等功能,需要对某条数据变动之后看到变化的数据。

我想到两个实现方法:使用反射(直接舍弃,效率太低),使用阿里fastjson的JSONObject

所需东西:维护一个对象字段及字段含义的枚举,每条数据的对象模版

maven依赖

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.69</version>
</dependency>

对象模版

@Data
@NoArgsConstructor
public class OptRecord {
    private String type;
    private Double ammount;
    private List<String> urls;
    private String remark;
}

枚举

public enum OptRecordEnum {
    URLS("urls", "图片凭证"),
    TYPE("type", "操作款项类型"),
    AMMOUNT("ammount", "金额"),
    REMARK("remark", "备注");
    private String filedName;
    private String filedMean;
    public static OptRecordEnum getOptRecordEnumByFiledName(String filedName){
        for (OptRecordEnum optRecordEnum : values()) {
            if (optRecordEnum.getFileName().equals(filedName)){
                return optRecordEnum;
            }
        }
        return null;
    }
    OptRecordEnum(final String filedName, final String filedMean) {
        this.filedName = filedName;
        this.filedMean = filedMean;
    }
    public String getFileName() {
        return this.filedName;
    }
    public String getFiledMean() {
        return this.filedMean;
    }
}

测试demo

public class TestCompare {
    public static void main(String[] args) throws Exception {
        OptRecord optRecord1 = new OptRecord();
        optRecord1.setRemark("321");
        optRecord1.setAmmount(123.00);
        optRecord1.setType("123");
        optRecord1.setUrls(Arrays.asList("321", "123"));
        OptRecord optRecord2 = new OptRecord();
        optRecord2.setRemark("123");
        optRecord2.setAmmount(124.00);
        optRecord2.setType("123");
        optRecord2.setUrls(Arrays.asList("1", "123"));
        JSONObject jsonObject1 = JSON.parseObject(JSON.toJSONString(optRecord1));
        JSONObject jsonObject2 = JSON.parseObject(JSON.toJSONString(optRecord2));
        List<Comparison> comparisons = new ArrayList<>();
        for (Map.Entry<String, Object> stringObjectEntry : jsonObject1.entrySet()) {
            if (!stringObjectEntry.getValue().equals(jsonObject2.get(stringObjectEntry.getKey()))){
                String key = stringObjectEntry.getKey();
                comparisons.add(new Comparison(key, OptRecordEnum.getOptRecordEnumByFiledName(key).getFiledMean(), jsonObject1.get(key), jsonObject2.get(key)));
            }
        }
        for (Comparison comparison : comparisons) {
            OptRecordEnum optRecordEnumByFiledName = OptRecordEnum.getOptRecordEnumByFiledName(comparison.getFiledName());
            //此处可以save db
            System.out.println(optRecordEnumByFiledName.getFiledMean() + ":" + comparison.getBeforeValue() + "->" + comparison.getAfterValue());
        }
    }
}

##运行结果

1.png

##反射实现方法

public class TestCompare {
    public static List<Comparison> compareObj(Object beforeObj, Object afterObj) throws Exception{
        List<Comparison> diffs = new ArrayList<>();
        if(beforeObj == null) {
            throw new RuntimeException("原对象不能为空");
        }
        if(afterObj == null) {
            throw new RuntimeException("新对象不能为空");
        }
        if(!beforeObj.getClass().isAssignableFrom(afterObj.getClass())){
            throw new RuntimeException("两个对象不相同,无法比较");
        }
        //取出属性
        Field[] beforeFields = beforeObj.getClass().getDeclaredFields();
        Field[] afterFields = afterObj.getClass().getDeclaredFields();
        Field.setAccessible(beforeFields, true);
        Field.setAccessible(afterFields, true);
        //遍历取出差异值
        if(beforeFields != null && beforeFields.length > 0){
            for(int i=0; i<beforeFields.length; i++){
                System.out.println(JSON.toJSONString(beforeFields[i].getName()) + ":" + JSON.toJSONString(afterFields[i].getName()));
                Object beforeValue = beforeFields[i].get(beforeObj);
                Object afterValue = afterFields[i].get(afterObj);
                if((beforeValue != null && !"".equals(beforeValue) && !beforeValue.equals(afterValue)) || ((beforeValue == null || "".equals(beforeValue)) && afterValue != null)){
                    Comparison comparison = new Comparison();
                    comparison.setFiledName(beforeFields[i].getName());
                    comparison.setBeforeValue(beforeValue);
                    comparison.setAfterValue(afterValue);
                    diffs.add(comparison);
                }
            }
        }
        return diffs;
    }
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    static class Comparison{
        private String filedName;
        private String filedMean;
        private Object beforeValue;
        private Object afterValue;
    }
}
相关文章
|
1月前
|
Prometheus 监控 Kubernetes
监控对象都有哪些分类
【10月更文挑战第9天】本文介绍了多种监控类型及其特点,包括业务监控、应用监控、组件监控、资源监控、设备监控及网络监控。
|
6月前
|
机器学习/深度学习 缓存 文字识别
印刷文字识别产品使用合集之标注阶段设定了两个独立的字段,但在返回的信息中却合并成了一个字段如何解决
印刷文字识别(Optical Character Recognition, OCR)技术能够将图片、扫描文档或 PDF 中的印刷文字转化为可编辑和可搜索的数据。这项技术广泛应用于多个领域,以提高工作效率、促进信息数字化。以下是一些印刷文字识别产品使用的典型场景合集。
|
6月前
|
运维 编译器 C#
C# 9.0中的本地函数属性:深化函数级别的控制
【1月更文挑战第17天】C# 9.0引入了本地函数属性的概念,允许开发者在本地函数上应用属性,从而进一步细化对函数行为的控制。这一新特性不仅增强了代码的可读性和可维护性,还为函数级别的编程提供了更多的灵活性。本文将探讨C# 9.0中本地函数属性的用法、优势以及可能的应用场景,帮助读者更好地理解并应用这一新功能。
基于参数关联关系动态呈现参数
本场景以计费方式为例,为您介绍资源编排如何在计费方式为包年包月时动态呈现包年包月的单位和时长。
|
Ubuntu Linux
用户操作轨迹记录
用户操作轨迹记录
212 1
用户操作轨迹记录
|
数据采集 NoSQL 大数据
数据预处理-航线类型操作类型-更新规则|学习笔记
快速学习数据预处理-航线类型操作类型-更新规则
336 0
|
数据可视化 C# Windows
C# 外部组件发生异常原因分析 [分析]
在项目中,用From2 启动 Report 正常,用From1 启动 Report 失败,日志: 2007-05-12 13:11:06 StartGenerateReportTask: System.Runtime.InteropServices.SEHException: 外部组件发生异常。
3650 0
|
机器学习/深度学习
2028. 找出缺失的观测数据 : 构造运用题
2028. 找出缺失的观测数据 : 构造运用题
Kam
枚举优化if-else if -else过程记录
枚举优化if-else if -else过程记录
Kam
226 0
|
SQL 监控 Java
SLS新增单位转换函数——消除数据转换烦恼
在日常工作中,经常会遇到数据单位或时间单位不一致的情况,当处理或分析此类数据时,往往费事费力,非常麻烦。 现在,SLS新增了单位转换函数,可以实现在不同单位之间轻松地进行转换、统一单位、格式化为可读文本,为用户减少不必要的数据转换工作,提升分析效率。
589 0