<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>4.5.11version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.8version>
dependency>
假设我们现在有个需求,信息变更审核功能,要求要记录信息变更 修改前 和 修改后 的值。
eg: 姓名:由 张三 变更为 李四
核心注解:
import java.lang.annotation.*;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ColumnName {
/**
* 字段名称
* @return
*/
String value() default "";
/**
* 当前字段的类型,如果是集合指定集合中的类型
* 基本类型 可不指定
* 用于复杂类型 eg: Object | List
Class<?> classType() default void.class;
/**
* 是否是集合类型
* 如果是集合类型必须指定其
* 1. 集合中存放的对象类型 {@link ColumnName#classType()}
* 2. 集合中的对象的唯一标识 {@link ColumnName#onlyMark()}
* @return
*/
boolean isList() default false;
/**
* 如果是集合类型还要知道该集合对象中什么字段可以确定唯一 可多个 逗号隔开 eg: id,name
* @return
*/
String onlyMark() default "";
/**
* 如果是日期类型 可自定义日期格式
* 默认 yyyy-MM-dd HH:mm:ss
* 常用格式,包括:
* HH:mm:ss
* yyyy-MM-dd
* yyyy-MM-dd HH:mm
* yyyy-MM-dd HH:mm:ss
* yyyy-MM-dd HH:mm:ss.SSS
* @return
*/
String dateFormat() default "yyyy-MM-dd HH:mm:ss";
}
有了以上注解后我们来看我们要对比的实体类
假设我们现在有一个人的类
里面包含 编号、姓名、年龄、生日,以及这个人养的一条狗,以及一个女朋友的List集合
人类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
@ColumnName("编号")
private Integer id;
@ColumnName("姓名")
private String name;
@ColumnName("年龄")
private Integer age;
@ColumnName(value = "生日", dateFormat = "yyyy-MM-dd")
private Date birthDay;
@ColumnName(value = "狗", classType = Dog.class)
private Dog dog;
@ColumnName(value = "女朋友", isList= true, classType = GirlFriend.class, onlyMark = "id,age")
private List<GirlFriend> girlFriends;
}
狗类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dog {
@ColumnName("狗名字")
private String name;
}
女朋友类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GirlFriend {
@ColumnName("编号")
private Integer id;
@ColumnName("姓名")
private String name;
@ColumnName("年龄")
private Integer age;
}
创建一个存放不同的类,又来存放最终对比出来的不同的结果
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Different {
// 字段
private String field;
// 对应的字段提示 eg: 姓名
private String fieldName;
// 变更前的值
private String oldData;
// 变更后的值
private String newData;
@Override
public String toString() {
return "数据库字段:'" + field + "', 字段:'" + fieldName + '\'' + ", 老值:'" + oldData + '\'' + ", 新值:'" + newData + '\'';
}
}
现在我们有一个修改之前的人对象和修改之后的人对象
要求:获取到两者的不同之处
public static void main(String[] args) throws Exception {
// 修改之前的对象
Person person1 = new Person();
person1.setId(1);
person1.setName("张三");
person1.setBirthDay(DateUtil.parse("2018-05-05"));
person1.setAge(18);
Dog dog = new Dog();
dog.setName("张三的狗");
person1.setDog(dog);
person1.setGirlFriends(Arrays.asList(
new GirlFriend(1, "小花", 19)
, new GirlFriend(2, "小丽", 20)
, new GirlFriend(3, "小青", 10)
));
// 修改之后的对象
Person person2 = new Person();
person2.setId(1);
person2.setName("张三");
person2.setBirthDay(DateUtil.parse("2018-05-05"));
person2.setAge(18);
Dog dog2 = new Dog();
dog2.setName("张三的狗");
person2.setDog(dog2);
person2.setGirlFriends(Arrays.asList(
new GirlFriend(1, "小花", 19)
, new GirlFriend(2, "小丽", 20)
, new GirlFriend(3, "小青", 10)
));
// 获取两个对象的不同
getDifferents(person1, person2, Person.class).forEach(System.out::println);
}
我们修改 person2 的name 修改为: 张三三 并执行 Main
可以得到以下结果
继续修改 person2 的birthday 修改为 :2018-10-10 并执行 Main
可以得到以下结果
如果日期的格式不符合预期结果 可以在@ColumnName#dateFormat() 指定其日期的格式
继续修改dog2的name 为:张三的大黑狗
可以得到以下结果
复杂的类型需要在实体类中指定类型 eg:@ColumnName#classType()
我们继续修改女朋友的集合中的数据
将名字叫小花的女朋友名字修改为 花姑娘 结果
由于集合会出现 添加、修改、删除三种 不同的情况
所有这里再添加一个接口
public interface FieldNameHandler {
/**
* 增
*/
String ADDED_PREFIX = "addedPrefix";
/**
* 删
*/
String DELETE_PREFIX = "deletePrefix";
/**
* 改
*/
String UPDATE_PREFIX = "updatePrefix";
/**
* 处理添加字段名
* @return 添加数据时的字段名前缀
*/
String addedPrefix();
/**
* 处理删除字段名
* @return 删除数据时的字段名前缀
*/
String deletePrefix();
/**
* 处理修改字段名
* @return 修改数据时的字段名前缀
*/
String updatePrefix();
}
让我们的GirlFriend 实现 FieldNameHandler
实现后我们的GirlFriend变成
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GirlFriend implements FieldNameHandler {
@ColumnName("编号")
private Integer id;
@ColumnName("姓名")
private String name;
@ColumnName("年龄")
private Integer age;
@Override
public String addedPrefix() {
return StrUtil.format("添加女朋友 ");
}
@Override
public String deletePrefix() {
return StrUtil.format("删除女朋友 ");
}
/**
* 复杂类型的 变更字段 可以自定义
* @return 返回值会加上 {@link ColumnName#value()} 的值
* 如果当前对象的姓名改了 字段的值就为 : 修改编号为:1的女朋友
*/
@Override
public String updatePrefix() {
return StrUtil.format("修改编号为:" + this.id + "的女朋友 ");
}
}
我们再次运行Main
字段本来的值是@ColumnName#value() 的值 姓名 实现了FieldNameHandler接口后如果发现是新增、删除、修改
会自动调用 实现类中的addedPrefix、deletePrefix、updatePrefix 并 加上@ColumnName#value()
添加删除女朋友这里就不模拟了
最后贴上getDifferents 写的比较粗糙 见谅
/**
* 数据修改对比统计
*
* @param oldT 修改前
* @param newT 修改后
* @param className 类名
* @param
* @return
* @throws Exception
*/
public static <T> List<Different> getDifferents(T oldT, T newT, Class<?> className) throws IllegalAccessException, InstantiationException {
// 存放处理结果
List<Different> differents = new ArrayList<>();
// 获取当前类的所有字段
Field[] fields = className.getDeclaredFields();
for (Field f : fields) {
// 过滤 static、 final、private static final字段
if (f.getModifiers() == Modifier.FINAL || f.getModifiers() == Modifier.STATIC || f.getModifiers() == (Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL)) {
continue;
}
// 获取当前字段注解
ColumnName annotationColumn = f.getAnnotation(ColumnName.class);
if (annotationColumn == null) {
continue;
}
// 反射获取当前老对象的字段值
Object oldV = ReflectUtil.getFieldValue(oldT, f.getName());
// 反射获取当前新对象的字段值
Object newV = ReflectUtil.getFieldValue(newT, f.getName());
// 当前字段的类型 用于复杂类型 eg: Object List Set
final Class<?> dataType = annotationColumn.classType();
final String typeName = annotationColumn.classType().getTypeName();
// 如果他是集合类型
if (annotationColumn.isList()) {
// 二次校验是否是集合类型
if (Objects.isNull(oldV)) oldV = new ArrayList<>();
if (Objects.isNull(newV)) newV = new ArrayList<>();
final boolean isCollection = Collection.class.isAssignableFrom(oldV.getClass());
if (isCollection) {
final List oldList = (List) oldV;
final List newList = (List) newV;
// 两个集合都是空的继续处理剩余字段
if (CollectionUtil.isEmpty(oldList) && CollectionUtil.isEmpty(newList)) {
continue;
}
// 老的是空的 新的有值 说明是集合中新添加了值 并不存在修改操作
if (CollectionUtil.isEmpty(oldList) && CollectionUtil.isNotEmpty(newList)) {
for (Object n : newList) {
differents.addAll(getDifferents(n.getClass().newInstance(), n, dataType));
}
continue;
}
// 新的是空的 老的有值 说明是集合中的值全部删除了
if (CollectionUtil.isNotEmpty(oldList) && CollectionUtil.isEmpty(newList)) {
for (Object o : oldList) {
differents.addAll(getDifferents(o, o.getClass().newInstance(), dataType));
}
continue;
}
// 创建临时集合
List allList = new ArrayList();
allList.addAll(oldList);
allList.addAll(newList);
for (Object o : oldList) {
int matchCount = 0;
for (Object n : newList) {
final String key = annotationColumn.onlyMark();
// 如果存在多个唯一标识字段特殊处理
if (key.contains(",")) {
final String[] keys = key.split(",");
boolean isSuccess = true;
for (String s : keys) {
final Object o1 = ReflectUtil.getFieldValue(o, s);
final Object n1 = ReflectUtil.getFieldValue(n, s);
// 有一个不匹配跳出循环
if (!o1.equals(n1)) {
isSuccess = false;
break;
}
}
if (isSuccess) {
// 匹配到就--
matchCount--;
differents.addAll(getDifferents(o, n, dataType));
} else {
// 没有匹配到就++
matchCount++;
// 如果不是一个对象 就去判断该新对象是否是之前已经判断过了的对象
final long count = allList.stream().filter(e -> {
for (String s : keys) {
final Object o1 = ReflectUtil.getFieldValue(e, s);
final Object n1 = ReflectUtil.getFieldValue(n, s);
// 有一个不匹配跳出循环
if (!o1.equals(n1)) {
return false;
}
}
return true;
}).count();
if (count < 2) {
// 说明是新添加了一个
differents.addAll(getDifferents(o.getClass().newInstance(), n, dataType));
allList.add(n);
}
}
} else {
final Object o1 = ReflectUtil.getFieldValue(o, key);
final Object n1 = ReflectUtil.getFieldValue(n, key);
if (o1.equals(n1)) {
// 匹配到就--
matchCount--;
differents.addAll(getDifferents(o, n, dataType));
} else {
// 没有匹配到就++
matchCount++;
// 如果不是一个对象 就去判断该新对象是否是之前已经判断过了的对象
final long count = allList.stream().filter(e -> ReflectUtil.getFieldValue(e, key).equals(ReflectUtil.getFieldValue(n, key))).count();
if (count < 2) {
// 说明是新添加了一个
differents.addAll(getDifferents(o.getClass().newInstance(), n, dataType));
allList.add(n);
}
}
}
}
// 没配到说明是老数据本来有新的数据删除了
if (matchCount == newList.size()) {
differents.addAll(getDifferents(o, o.getClass().newInstance(), dataType));
}
}
}
}
// 不能是 void 和 基本类型
if (!"void".equals(typeName) && !ClassUtil.isBasicType(dataType)) {
final Class<?> aClass = annotationColumn.classType();
differents.addAll(getDifferents(oldV, newV, aClass));
continue;
}
// 检查新老对象的值是否一致 不一致记录
if (!Objects.equals(newV, oldV)) {
// 字段名字
String fieldName = StrUtil.isNotEmpty(annotationColumn.value()) ? annotationColumn.value() : f.getName();
// 获取当前类所实现的所有接口
final Class<?>[] interfaces = className.getInterfaces();
// 是否实现了FieldNameHandler接口,如果实现了FieldNameHandler接口 就追加调用 FieldNameHandler#handle()
final long count = Stream.of(interfaces).filter(a -> a.equals(FieldNameHandler.class)).count();
if (count > 0) {
Object invoke = null;
// 添加了数据
if (oldT.getClass().newInstance().equals(oldT)) {
invoke = ReflectUtil.invoke(oldT, FieldNameHandler.ADDED_PREFIX);
}
// 删除了数据
else if (newT.getClass().newInstance().equals(newT)) {
invoke = ReflectUtil.invoke(oldT, FieldNameHandler.DELETE_PREFIX);
}
// 修改了数据
else {
invoke = ReflectUtil.invoke(oldT, FieldNameHandler.UPDATE_PREFIX);
}
if (Objects.nonNull(invoke)) {
fieldName = invoke + fieldName;
}
}
// 老对象值
String oldValue = null != oldV ? oldV.toString() : "";
// 新对象值
String newValue = null != newV ? newV.toString() : "";
// 日期格式特殊处理
if (oldV instanceof Date && newV instanceof Date) {
if (StrUtil.isNotEmpty(oldValue)) {
oldValue = DateUtil.format((Date) oldV, annotationColumn.dateFormat());
}
if (StrUtil.isNotEmpty(newValue)) {
newValue = DateUtil.format((Date) newV, annotationColumn.dateFormat());
}
}
// 添加处理结果中
differents.add(new Different(f.getName(), fieldName, oldValue, newValue));
}
}
return differents;
}
转载注明出处 谢谢。