一、业务需求

需要将业务表每次更新操作的前后记录进行保存,写入更新历史表中

方便用户查阅该业务记录发生的历史变化

二、代码实现

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor; import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors; /**
* 判断实体记录是否变更工具类
*/
public class DtoChangeUtil { public static <Dto> void dtoCompare(Dto beforeDto, Dto afterDto, Consumer<CompareInfo> consumer) {
Class<?> dtoClass = beforeDto.getClass(); List<Field> requiredFields = Arrays.stream(dtoClass.getDeclaredFields())
.filter(f -> Objects.nonNull(f.getAnnotation(CompareRequire.class)))
.collect(Collectors.toList()); for (Field requiredField : requiredFields) {
requiredField.setAccessible(true);
String labelName = requiredField.getAnnotation(CompareRequire.class).labelName();
String fieldName = requiredField.getName();
Class<?> fieldType = requiredField.getType();
try {
Object beforeVal = requiredField.get(beforeDto);
Object afterVal = requiredField.get(afterDto); boolean[] isEquals = compareFieldIsEquals(beforeVal, afterVal, fieldType);
// if (isEquals[0]) continue;
consumer.accept(CompareInfo.builder()
.labelName(labelName)
.fieldName(fieldName)
.isEquals(isEquals[0])
.before(isEquals[1] ? "" : beforeVal.toString())
.after(isEquals[2] ? "" : afterVal.toString())
.build()); } catch (Exception e) {
e.printStackTrace();
}
}
} /**
* 判断该字段前后值是否相等
* @param beforeVal 之前值
* @param afterVal 之后值
* @param fieldType 类型
* @return [是否相等, before是否空值, after是否空值]
*/
private static boolean[] compareFieldIsEquals(Object beforeVal, Object afterVal, Class<?> fieldType) {
boolean isEmptyBeforeVal = Objects.isNull(beforeVal);
boolean isEmptyAfterVal = Objects.isNull(afterVal);
if (isEmptyBeforeVal && isEmptyAfterVal) return new boolean[] { true, true, true };
else if (isEmptyBeforeVal && !isEmptyAfterVal) return new boolean[] { false, true, false };
else if (!isEmptyBeforeVal && isEmptyAfterVal) return new boolean[] { false, false, true }; boolean isString = String.class.equals(fieldType);
boolean isDate = Date.class.equals(fieldType);
boolean isBigDecimal = BigDecimal.class.equals(fieldType);
boolean isBoolean = boolean.class.equals(fieldType) || Boolean.class.equals(fieldType);
boolean isByte = byte.class.equals(fieldType) || Byte.class.equals(fieldType);
boolean isChar = char.class.equals(fieldType) || Character.class.equals(fieldType);
boolean isShort = short.class.equals(fieldType) || Short.class.equals(fieldType);
boolean isInt = int.class.equals(fieldType) || Integer.class.equals(fieldType);
boolean isLong = long.class.equals(fieldType) || Long.class.equals(fieldType);
boolean isFloat = float.class.equals(fieldType) || Float.class.equals(fieldType);
boolean isDouble = double.class.equals(fieldType) || Double.class.equals(fieldType);
/* 其他类型以此类推 */
if (isString || isDate) return new boolean[] { beforeVal.equals(afterVal), false, false };
int compareResult = 0;
if (isBigDecimal) compareResult = ((BigDecimal) beforeVal).compareTo((BigDecimal) afterVal);
if (isBoolean) compareResult = ((Boolean) beforeVal).compareTo((Boolean) afterVal);
if (isByte) compareResult = ((Byte) beforeVal).compareTo((Byte) afterVal);
if (isChar) compareResult = ((Character) beforeVal).compareTo((Character) afterVal);
if (isShort) compareResult = ((Short) beforeVal).compareTo((Short) afterVal);
if (isInt) compareResult = ((Integer) beforeVal).compareTo((Integer) afterVal);
if (isLong) compareResult = ((Long) beforeVal).compareTo((Long) afterVal);
if (isFloat) compareResult = ((Float) beforeVal).compareTo((Float) afterVal);
if (isDouble) compareResult = ((Double) beforeVal).compareTo((Double) afterVal);
return new boolean[] { compareResult == 0, false, false };
} /**
* 需要比较注解:
* 对实体类需要进行比较的字段进行注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public static @interface CompareRequire {
String labelName(); /* 字段业务名称 */
} /**
* 需要比较注解:
* 比较信息
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static final class CompareInfo {
private String labelName;
private boolean isEquals;
private String fieldName;
private String before;
private String after;
} /**
* 测试用例,模拟同一条记录,表单提交的和数据库保存的内容发生了哪些变化
* 仅注解了[需要比较]的字段才会处理
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
static class TestDemo {
private Integer id;
@CompareRequire(labelName = "业务编号")
private String code;
@CompareRequire(labelName = "业务时间")
private Date busTime;
@CompareRequire(labelName = "操作用户")
private String userName;
}
public static void main(String[] args) {
Date date = new Date();
TestDemo t1 = TestDemo.builder().id(1).busTime(new Date(date.getTime() + 1000)).code("1234").userName("admin").build();
TestDemo t2 = TestDemo.builder().id(1).busTime(new Date(date.getTime() - 3000)).code("5678").userName("admin").build();
dtoCompare(t1, t2, System.out::println);
}
}

  

【Java】比较业务实体信息变化的工具类的更多相关文章

  1. Java语言Lang包下常用的工具类介绍_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 无论你在开发哪中 Java 应用程序,都免不了要写很多工具类/工具函数.你可知道,有很多现成的工具类可用,并且代码质量都 ...

  2. java调用kettle的job和transfer工具类

    package com.woaiyitiaocai.util; import java.util.Map; import java.util.UUID; import org.apache.log4j ...

  3. Java Class与反射相关的一些工具类

    package com.opslab.util; import org.apache.log4j.Logger; import java.io.File;import java.io.IOExcept ...

  4. java关闭资源,自制关闭资源工具类

    在网上看到一篇关于关闭资源的正确方式:http://blog.csdn.net/bornforit/article/details/6896775 该博文中的总结: (1)使用finally块来关闭物 ...

  5. Java手写一个批量获取数据工具类

    1. 背景 偶尔会在公司的项目里看到这样的代码 List<Info> infoList = new ArrayList<Info>(); if (infoidList.size ...

  6. Java操作zip压缩和解压缩文件工具类

    需要用到ant.jar(这里使用的是ant-1.6.5.jar) import java.io.File; import java.io.FileInputStream; import java.io ...

  7. java中模拟http(https)请求的工具类

    在java中,特别是java web中,我们经常需要碰到的一个场景是我们需要从服务端去发送http请求,获取到数据,而不是直接从浏览器输入请求网址获得相应.比如我们想访问微信接口,获取其返回信息. 在 ...

  8. 【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore

    前言 JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是CountDownLatch.CyclicBarrier和Semaphore,其原理都是使用了AQS来实现,下面分别进行 ...

  9. JAVA调用操作javascript (JS)工具类

    import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import ...

  10. Java基础知识强化92:日期工具类的编写和测试案例

    1. DateUtil.java,代码如下: package cn.itcast_04; import java.text.ParseException; import java.text.Simpl ...

随机推荐

  1. tomcat部署Jenkins

    安装环境 jdk 1.8 tomcat 9.0 jenkins 2.290 准备工作 安装好Tomcat,8080端口启动 安装好jdk,配置好环境变量 ECS服务器安全组放开8080端口 关闭防火墙 ...

  2. 如何防止 Elasticsearch 服务 OOM ?

    ES 和传统关系型数据库有很多区别, 比如传统数据中普遍都有一个叫"最大连接数"的设置.目的是使数据库系统工作在可控的负载下,避免出现负载过高,资源耗尽,谁也无法登录的局面. 那 ...

  3. OAuth + Security - 2 - 资源服务器配置

    PS:此文章为系列文章,建议从第一篇开始阅读. 资源服务器配置 @EnableResourceServer 注解到一个@Configuration配置类上,并且必须使用ResourceServerCo ...

  4. AWS Ubuntu22.04安装Mysql及配置远程连接、SCP上传文件

    一.升级apt资源包 sudo apt update 二.安装MySQL sudo apt install mysql-server 三.启动MySQL服务 sudo service mysql st ...

  5. Css var 简述

    Css var 语法 var(custom-property-name, value) - custom-property-name 必须 变量必须以 --开头 后面可以是英文.数字连接符,区分大小写 ...

  6. tampermonkey脚本 百度搜索屏蔽CSDN

    // ==UserScript==// @name         屏蔽CSDN// @namespace    http://tampermonkey.net/// @version      20 ...

  7. Shell脚本实现Linux回收站

    前言 到目前为止,非图形化的Linux还没有回收站的命令. 那么,我们如果不小心将某一个文件删掉了,那就只能数据恢复了.如果这也不行,那就等着哭吧. 最新代码在我的github:https://git ...

  8. EthernetIP IO从站设备数据 转 Modbus RTU TCP项目案例

    1         案例说明 1.   设置网关采集EthernetIP IO设备数据 2.   把采集的数据转成Modbus协议转发给其他系统. 2        VFBOX网关工作原理 VFBOX ...

  9. Apache Kylin(二)在EMR上搭建Kylin

    EMR上搭建kylin 1. 启动EMR集群 根据官网说明: http://kylin.apache.org/docs21/install/kylin_aws_emr.html 启动EMR时,若是 h ...

  10. 3568F-PCIe 5G通信测试手册