java保存实现变更记录

java保存实现变更记录

前言

  最近遇到一个需求,要求编写保存变更记录的功能,记录下哪些字段由什么值变为了什么值,经过参考网上的解决方案,将部分核心代码记录下来。

实现方案

  1. 自定义注解@LogCompar:配置了要比较的字段名称,日期格式,码值转换,默认值等;
  2. 编写工具类:通过java反射机制,获取要比较对象中所有添加了@LogCompar的属性,并进行比较记录;
  3. 单元测试:要比较的实体类添加@LogCompar注解,测试。

代码实现

  1. 创建自定义注解:
/*** @Describe 变更记录注解:用于注释在要比较变更的对象的属性上* @Author xlma* @Date 2023/02/06 21:45* @Version 1.0*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogCompar {/*** 属性名称*/String value();/*** 日期格式, 如: yyyy-MM-dd*/String dateFormat() default "";/*** 读取内容转表达式 (如: 0=男,1=女,2=未知)*/String readConverterExp() default "";/*** 分隔符,readConverterExp表达式的分隔符*/String separator() default ",";/*** 当值为空时的默认值*/String defaultValue() default "null";
}
  1. 编写工具类
/*** @Describe 添加变更记录工具类* @Author xlma* @Date 2023/2/6 21:32* @Version 1.0*/
@Slf4j
public class RecordUtils {/*** 注解列表(添加了@LogCompar的属性集合,数组的第一位是属性Field,第二位是注解LogCompar)*/private static List<Object[]> fields;/*** 比较变更前后的数据,记录下添加了@LogCompar注解的属性,由原来的什么值变为了现在的什么值** @param oldObj 原数据对象* @param newObj 新数据对象*/public static String addChangeRecord(Object oldObj, Object newObj) {try {// 获取Class对象Class<?> oldClazz = oldObj.getClass();Class<?> newClazz = newObj.getClass();if (!oldClazz.equals(newClazz)) {throw new RuntimeException("请传入两个相同的实体类对象!");}// 获取加了注解的属性集合fields = getFields(oldClazz);if (fields.size() == 0) {throw new RuntimeException("没有找到要对比的属性!");}// 定义变量:变更描述信息StringBuilder info = new StringBuilder();// 根据注解列表,循环比较属性是否变化for (Object[] os : fields) {Field field = (Field) os[0];LogCompar logCompar = (LogCompar) os[1];// 获取新旧的属性值Object oldValue = field.get(oldObj);Object newValue = field.get(newObj);if (oldValue == null) {oldValue = logCompar.defaultValue();}if (newValue == null) {newValue = logCompar.defaultValue();}// 若新值和旧值相等,则跳过if (oldValue == newValue || newValue.equals(oldValue)) {continue;}// 若是BigDecimal类型,需要compareTo方法判断是否相等,相等则跳过if (field.getType() == BigDecimal.class && ((BigDecimal) newValue).compareTo((BigDecimal) oldValue) == 0) {continue;}// 若属性值有变化,则判断是否有码值,if (StringUtils.isNotBlank(logCompar.readConverterExp())) {oldValue = convertByExp(oldValue.toString(), logCompar.readConverterExp(), logCompar.separator());newValue = convertByExp(newValue.toString(), logCompar.readConverterExp(), logCompar.separator());} else if (StringUtils.isNotBlank(logCompar.dateFormat())) {oldValue = DateHelper.format(oldValue, logCompar.dateFormat());newValue = DateHelper.format(newValue, logCompar.dateFormat());}info.append(String.format("%s:由%s 变为了 %s;", logCompar.value(), oldValue, newValue));}// 此处可以返回info或直接保存日志表处理return info.toString();} catch (IllegalAccessException e) {log.error(e.getMessage());throw new RuntimeException(e.getMessage());}}/*** 获取注解属性信息*/private static List<Object[]> getFields(Class<?> clazz) {// 定义加了@LogCompar注解的属性集合List<Object[]> fields = new ArrayList<Object[]>();// 定义临时的属性集合,包含了当前类及其父类的所有属性List<Field> tempFields = new ArrayList<>();tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));// 遍历临时属性集合,判断遍历出的属性上是否有@LogCompar注解,若有,则将当前属性及注解添加到fields中for (Field field : tempFields) {// 若当前属性field上添加了@LogCompar注解,则isAnnotationPresent()方法返回trueif (field.isAnnotationPresent(LogCompar.class)) {LogCompar logCompar = field.getAnnotation(LogCompar.class);if (logCompar != null) {// 设置可以访问私有private的属性field.setAccessible(true);fields.add(new Object[]{field, logCompar});}}}return fields;}/*** 解析导出值 0=男,1=女,2=未知** @param propertyValue 参数值* @param converterExp  翻译注解* @param separator     分隔符* @return 解析后值*/private static String convertByExp(String propertyValue, String converterExp, String separator) {StringBuilder stringBuilder = new StringBuilder();String[] convertSource = converterExp.split(",");for (String item : convertSource) {String[] itemArray = item.split("=");if (StringUtils.containsAny(separator, propertyValue)) {for (String value : propertyValue.split(separator)) {if (itemArray[0].equals(value)) {stringBuilder.append(itemArray[1] + separator);break;}}} else if (itemArray[0].equals(propertyValue)) {return itemArray[1];}}return StringUtils.stripEnd(stringBuilder.toString(), separator);}
}
  1. 创建实体类,以用户表为例
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {private Long id;private String username;@LogCompar("真实姓名")private String realName;@LogCompar(value = "性别", readConverterExp = "0=男,1=女,2=未知")private Integer sex;@LogCompar("邮箱")private String email;@LogCompar(value = "薪水")private BigDecimal salary;@LogCompar(value = "创建时间", dateFormat = "yyyy-MM-dd HH:mm:ss")private LocalDateTime createTime;
}
  1. 单元测试
@Test
void recordEncrypt() {User oldUser = new User(1L, "admin", "张三", 1, "123@qq.com", BigDecimal.valueOf(12), LocalDateTime.now());User newUser = new User(2L, "admin", "李四", 2, null, BigDecimal.valueOf(12.00), LocalDateTime.of(2022, 2, 12, 5, 12, 5));System.out.println(RecordUtils.addChangeRecord(oldUser, newUser));
}
  1. 结果
真实姓名:由张三 变为了 李四;性别:由女 变为了 未知;邮箱:由123@qq.com 变为了 null;创建时间:由2023-03-12 13:53:03 变为了 2022-02-12 05:12:05;


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部