【Java】比较业务实体信息变化的工具类
一、业务需求
需要将业务表每次更新操作的前后记录进行保存,写入更新历史表中
方便用户查阅该业务记录发生的历史变化
二、代码实现
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】比较业务实体信息变化的工具类的更多相关文章
- Java语言Lang包下常用的工具类介绍_java - JAVA
文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 无论你在开发哪中 Java 应用程序,都免不了要写很多工具类/工具函数.你可知道,有很多现成的工具类可用,并且代码质量都 ...
- java调用kettle的job和transfer工具类
package com.woaiyitiaocai.util; import java.util.Map; import java.util.UUID; import org.apache.log4j ...
- Java Class与反射相关的一些工具类
package com.opslab.util; import org.apache.log4j.Logger; import java.io.File;import java.io.IOExcept ...
- java关闭资源,自制关闭资源工具类
在网上看到一篇关于关闭资源的正确方式:http://blog.csdn.net/bornforit/article/details/6896775 该博文中的总结: (1)使用finally块来关闭物 ...
- Java手写一个批量获取数据工具类
1. 背景 偶尔会在公司的项目里看到这样的代码 List<Info> infoList = new ArrayList<Info>(); if (infoidList.size ...
- Java操作zip压缩和解压缩文件工具类
需要用到ant.jar(这里使用的是ant-1.6.5.jar) import java.io.File; import java.io.FileInputStream; import java.io ...
- java中模拟http(https)请求的工具类
在java中,特别是java web中,我们经常需要碰到的一个场景是我们需要从服务端去发送http请求,获取到数据,而不是直接从浏览器输入请求网址获得相应.比如我们想访问微信接口,获取其返回信息. 在 ...
- 【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore
前言 JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是CountDownLatch.CyclicBarrier和Semaphore,其原理都是使用了AQS来实现,下面分别进行 ...
- JAVA调用操作javascript (JS)工具类
import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import ...
- Java基础知识强化92:日期工具类的编写和测试案例
1. DateUtil.java,代码如下: package cn.itcast_04; import java.text.ParseException; import java.text.Simpl ...
随机推荐
- C# 指针简单使用
1. 使用unsafe C# 支持 unsafe 上下文,你可在其中编写不可验证的代码. 在 unsafe 上下文中,代码可使用指针.分配和释放内存块,以及使用函数指针调用方法. C# 中的不安全代码 ...
- JavaSE什么是面向对象?
目录 重点!!!面向对象 面向过程与面向对象 面向过程的程序思想 面向对象程序思想 类和对象(面向对象的核心概念) 类: 类的结构 对象:(Everything is an object) 重点!!! ...
- Qt OPC UA通信
介绍 OPC UA全称Open Platform Unified Architecture,开放平台统一架构,是工业自动化领域通用的数据交换协议,它有两套主要的通信机制:1.客户端-服务器通信:2.发 ...
- vue3 KeepAlive
在Vue.js 3中,<keep-alive> 是一个抽象组件,用于保留其子组件状态,防止在切换组件时销毁它们.这对于在页面间切换时保留组件的状态或避免重复渲染特定组件非常有用.<k ...
- 解决:Maven PKIX path building failed: sun.security.provider.certpath
在构建SpringBoot项目时,maven下载依赖会报 PKIX path building failed: sun.security.provider.certpath的错误. 使用https:/ ...
- 简单易懂的JSON框架
分享一个由本人编写的JSON框架. JSON反序列化使用递归方式来解析JSON字符串,不使用任何第三方JAR包,只使用JAVA的反射来创建对象(必须要有无参构造器),赋值,编写反射缓存来提升性 ...
- bean反射比较两个bean属性值的修改明细
1.期望:将[username]从'111'改成'222';将[address]从'这是一个测试数据'改成'这是一个真实数据'; 2.导入jar <dependency> <grou ...
- 日志切面接口和方法demo,切面内重新抛出异常
1. 定义切面 @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { Stri ...
- Linux 驱动:LED子系统
Linux 驱动:LED子系统 背景 在调试aw9523的时候,为了实现客户要的一个效果.需要修改驱动,但是大概看了一下驱动,但是因为不太熟悉LED子系统,所以有点云里雾里. 参考: https:// ...
- ZYNQ:使用 PetaLinux 构建Linux项目
参考文档:ug1144-petalinux-tools-reference-guide.pdf 环境安装 tofrodos iproute2 gawk gcc g++ git make net-too ...