需求点
在很多场景中信息是不能轻易被修改的,修改时要么需要具备权限,要么需要审批,但是无论是哪种方式,修改前后的数据都是需要留有“案底”的,也就是说关键的信息被修改后是有修改记录的,一般修改记录会记录修改人、修改日期和修改的数据字段。
比如,修改一个人的姓名从“张三”变为了“李四”,那么在进行记录的时候,记录的信息可能如下:
姓名:(张三)=>(李四);性别:(女)=>(男);
这样就很好的体现出了修改了哪个字段,修改前后的数据分别是什么。关键的信息无论怎么修改都会有据可查,时间、人物、修改数据前后信息等。
判断被修改的数据
在页面中将数据修改并提交页面后,数据会从 JSP 传递到 Controller 中,这时数据还没有被更新到数据库中,从提交到 Controller 的对象中用数据的 id 从数据库中查出它现有的数据,然后使用现有的数据和提交的数据进行对比,就可以得到被修改过的数据字段有哪些了。
这里比较繁琐的是如何进行比对,逐个字段的使用 if 进行判断肯定不是好方法,毕竟字段太多的时候是要写死人的。那么如何进行,直接比较两个对象就行,源码我是从网上找的,好用,符合需求,源码如下:
1 /** 2 * 获取两个对象同名属性内容不相同的列表 3 * @param class1 对象1 4 * @param class2 对象2 5 * @return 6 * @throws ClassNotFoundException 7 * @throws IllegalAccessException 8 */ 9 public static List<Map<String ,Object>> compareTwoClass(Object class1, Object class2) { 10 List<Map<String,Object>> list=new ArrayList<Map<String, Object>>(); 11 // 获取对象的class 12 Class<?> clazz1 = class1.getClass(); 13 Class<?> clazz2 = class2.getClass(); 14 // 获取对象的属性列表 15 Field[] field1 = clazz1.getDeclaredFields(); 16 Field[] field2 = clazz2.getDeclaredFields(); 17 // 遍历属性列表field1 18 for(int i=0;i<field1.length;i++){ 19 // 遍历属性列表field2 20 for(int j=0;j<field2.length;j++){ 21 // 如果field1[i]属性名与field2[j]属性名内容相同 22 if(field1[i].getName().equals(field2[j].getName())){ 23 if(field1[i].getName().equals(field2[j].getName())){ 24 field1[i].setAccessible(true); 25 field2[j].setAccessible(true); 26 // 如果field1[i]属性值与field2[j]属性值内容不相同 27 try { 28 if (!compareTwo(field1[i].get(class1), field2[j].get(class2))){ 29 Map<String,Object> map2=new HashMap<String, Object>(); 30 map2.put("name",field1[i].getName()); 31 map2.put("old",field1[i].get(class1)); 32 map2.put("new",field2[j].get(class2)); 33 list.add(map2); 34 } 35 } catch (IllegalArgumentException e) { 36 // TODO Auto-generated catch block 37 e.printStackTrace(); 38 } catch (IllegalAccessException e) { 39 // TODO Auto-generated catch block 40 e.printStackTrace(); 41 } 42 break; 43 } 44 } 45 } 46 } 47 return list; 48 } 49 50 /** 51 * 对比两个数据是否内容相同 52 * 53 * @param object1,object2 54 * @return boolean类型 55 */ 56 public static boolean compareTwo(Object object1,Object object2){ 57 if ( object1 == null && object2 == null ) { 58 return true; 59 } 60 if ( object1 == null && object2 != null ) { 61 return false; 62 } 63 if ( object1.equals(object2) ) { 64 return true; 65 } 66 return false; 67 }
源码是从哪里找到的忘记了,在这里感谢愿意分享的网友,是你的源码让我快速解决了项目中的问题。(当时虽然不懂 Java,但是对于类似的处理我认为一定有相应的方法,而不是傻傻的去逐个比较,后来了解到这是 Java 中的反射,发现反射与注解真是好东西,用在项目当中会省很多事,现在我写 PHP 时,虽然 PHP 不支持注解,但是我也会通过反射去解析注释的方式,实现类似 Java 中注解的功能,真的很省事。)
属性解析
上面的函数会返回两个对象中属性值不同的 List,获得该列表后,再次遍历解析属性对应的字段含义,进而拼接成一个字符串就可以生成修改日志进行保存了。
通常情况下只要把类中的属性和属性对应的中文进行关联后就可以了,但是在 JeeSite 中存在字典类型,比如“男”和“女”,在页面上会显示“男”和“女”,而在数据库中可能是以 “0” 和 “1” 进行存储的,所以一般在选择“男”或“女”后页面提交的也是 “0” 或 “1”,以这种方式进行日志记录显然不直观,因此在这种情况下就需要将字段的中文和字典名也进行关联,这样就可以将字段中文匹配到字典的值的描述。
具体代码如下:
1 public String catModifyInfo(List<Map<String, Object>> list) { 2 Map<String, String> mapField = new HashMap<String, String>() {{ 3 // 类中的属性,属性对应的中文 4 put("sex","性别"); 5 }}; 6 Map<String, String> mapDict = new HashMap<String, String>() {{ 7 // 属性对应的中文,在JeeSite中字典的描述 8 put("性别", "SEX"); 9 }}; 10 11 // 构造的修改字符串 12 String modInfo = ""; 13 14 for ( Map<String, Object> mp : list) { 15 System.out.println(mp.get("name") + "---" + mp.get("old") + "---" + mp.get("new")); 16 System.out.println(mapField.get(mp.get("name"))); 17 18 // 判断修改的值是否为字典 19 if ( mapDict.containsKey(mapField.get(mp.get("name"))) ) { 20 String oldValue = mp.get("old").toString(); 21 String newValue = mp.get("new").toString(); 22 String type = mapDict.get(mapField.get(mp.get("name"))); 23 String oldStr = DictUtils.getDictLabel(oldValue, type, ""); 24 String newStr = DictUtils.getDictLabel(newValue, type, ""); 25 System.out.println(mapField.get(mp.get("name")) + ":(" + oldStr + ") => (" + newStr + ");"); 26 modInfo += mapField.get(mp.get("name")) + ":(" + oldStr + ") => (" + newStr + ");"; 27 } else { 28 modInfo += mapField.get(mp.get("name")) + ":(" + mp.get("old") + ") => (" + mp.get("new") + ");"; 29 } 30 } 31 32 return modInfo; 33 }
函数传入的参数是两个对象差异的属性,在循环进行解析并进行字符串拼接后,就可以获得对应的日志了。
调用方法
在 JeeSite 中提交数据后,无论是修改还是新建,都会调用相关 Controller 中的 save 方法,因此上面的方法需要在 save 方法中进行调用。
相同的方法如何判断当前是新建,还是修改呢?判断的方法就是判断传入的对象中是否有 id,如果有 id 则说明是修改,如果没有 id 则说明是新建。
具体代码如下:
1 /* 2 * 如果id不为空,则表示为修改 3 */ 4 if ( StringUtils.isNotBlank(newXxx.getId()) ) { 5 Xxx oldXxx = new Xxx(); 6 // 获取原来的信息 7 oldXxx = xxxService.get(newXxx.getId()); 8 9 // 比较修改后的信息和未修改的信息 10 List<Map<String, Object>> modList = compareTwoClass(oldXxx, newXxx); 11 // 生成差异信息 12 String strModifyInfo = catModifyInfo(modList); 13 // 输出差异字符串 14 System.out.println(strModifyInfo); 15 16 // 把修改记录保存到日志表中 17 // ... 18 }
有了以上的方式就可以实现修改信息前后的日志记录了,修改后的情况如下:
不过该方式并不完美,如果修改了表字段的名称或数量,那么代码也要相应的修改,如果新添加的字段有对应的字典,那么也要添加字典对应的关联,这样就需要每次修改代码,十分的不方便了。
解决的方式很简单,使用 JeeSite 中代码生成的功能,就可以解决该问题。
我的微信公众号:“码农UP2U”