- 开发过程中,我们通常会对系统操作人对系统的操作进行记录,记录操作前后某个字段的变化,如下图
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_": ["高一二班", "高一三班"]
}
我们可以看到,对象修改后与修改后,相关字段产生的变化,如果有不需要对比的字段,传入相同的参数既可