需求点

在很多场景中信息是不能轻易被修改的,修改时要么需要具备权限,要么需要审批,但是无论是哪种方式,修改前后的数据都是需要留有“案底”的,也就是说关键的信息被修改后是有修改记录的,一般修改记录会记录修改人、修改日期和修改的数据字段。

比如,修改一个人的姓名从“张三”变为了“李四”,那么在进行记录的时候,记录的信息可能如下:

姓名:(张三)=>(李四);性别:(女)=>(男);

这样就很好的体现出了修改了哪个字段,修改前后的数据分别是什么。关键的信息无论怎么修改都会有据可查,时间、人物、修改数据前后信息等。

判断被修改的数据

在页面中将数据修改并提交页面后,数据会从 JSP 传递到 Controller 中,这时数据还没有被更新到数据库中,从提交到 Controller 的对象中用数据的 id 从数据库中查出它现有的数据,然后使用现有的数据和提交的数据进行对比,就可以得到被修改过的数据字段有哪些了。

这里比较繁琐的是如何进行比对,逐个字段的使用 if 进行判断肯定不是好方法,毕竟字段太多的时候是要写死人的。那么如何进行,直接比较两个对象就行,源码我是从网上找的,好用,符合需求,源码如下:

 /**
* 获取两个对象同名属性内容不相同的列表
* @param class1 对象1
* @param class2 对象2
* @return
* @throws ClassNotFoundException
* @throws IllegalAccessException
*/
public static List<Map<String ,Object>> compareTwoClass(Object class1, Object class2) {
List<Map<String,Object>> list=new ArrayList<Map<String, Object>>();
// 获取对象的class
Class<?> clazz1 = class1.getClass();
Class<?> clazz2 = class2.getClass();
// 获取对象的属性列表
Field[] field1 = clazz1.getDeclaredFields();
Field[] field2 = clazz2.getDeclaredFields();
// 遍历属性列表field1
for(int i=0;i<field1.length;i++){
// 遍历属性列表field2
for(int j=0;j<field2.length;j++){
// 如果field1[i]属性名与field2[j]属性名内容相同
if(field1[i].getName().equals(field2[j].getName())){
if(field1[i].getName().equals(field2[j].getName())){
field1[i].setAccessible(true);
field2[j].setAccessible(true);
// 如果field1[i]属性值与field2[j]属性值内容不相同
try {
if (!compareTwo(field1[i].get(class1), field2[j].get(class2))){
Map<String,Object> map2=new HashMap<String, Object>();
map2.put("name",field1[i].getName());
map2.put("old",field1[i].get(class1));
map2.put("new",field2[j].get(class2));
list.add(map2);
}
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
}
}
}
}
return list;
} /**
* 对比两个数据是否内容相同
*
* @param object1,object2
* @return boolean类型
*/
public static boolean compareTwo(Object object1,Object object2){
if ( object1 == null && object2 == null ) {
return true;
}
if ( object1 == null && object2 != null ) {
return false;
}
if ( object1.equals(object2) ) {
return true;
}
return false;
}

源码是从哪里找到的忘记了,在这里感谢愿意分享的网友,是你的源码让我快速解决了项目中的问题。(当时虽然不懂 Java,但是对于类似的处理我认为一定有相应的方法,而不是傻傻的去逐个比较,后来了解到这是 Java 中的反射,发现反射与注解真是好东西,用在项目当中会省很多事,现在我写 PHP 时,虽然 PHP 不支持注解,但是我也会通过反射去解析注释的方式,实现类似 Java 中注解的功能,真的很省事。)

属性解析

上面的函数会返回两个对象中属性值不同的 List,获得该列表后,再次遍历解析属性对应的字段含义,进而拼接成一个字符串就可以生成修改日志进行保存了。

通常情况下只要把类中的属性和属性对应的中文进行关联后就可以了,但是在 JeeSite 中存在字典类型,比如“男”和“女”,在页面上会显示“男”和“女”,而在数据库中可能是以 “0” 和 “1” 进行存储的,所以一般在选择“男”或“女”后页面提交的也是 “0” 或 “1”,以这种方式进行日志记录显然不直观,因此在这种情况下就需要将字段的中文和字典名也进行关联,这样就可以将字段中文匹配到字典的值的描述。

具体代码如下:

 public String catModifyInfo(List<Map<String, Object>> list) {
Map<String, String> mapField = new HashMap<String, String>() {{
// 类中的属性,属性对应的中文
put("sex","性别");
}};
Map<String, String> mapDict = new HashMap<String, String>() {{
// 属性对应的中文,在JeeSite中字典的描述
put("性别", "SEX");
}}; // 构造的修改字符串
String modInfo = ""; for ( Map<String, Object> mp : list) {
System.out.println(mp.get("name") + "---" + mp.get("old") + "---" + mp.get("new"));
System.out.println(mapField.get(mp.get("name"))); // 判断修改的值是否为字典
if ( mapDict.containsKey(mapField.get(mp.get("name"))) ) {
String oldValue = mp.get("old").toString();
String newValue = mp.get("new").toString();
String type = mapDict.get(mapField.get(mp.get("name")));
String oldStr = DictUtils.getDictLabel(oldValue, type, "");
String newStr = DictUtils.getDictLabel(newValue, type, "");
System.out.println(mapField.get(mp.get("name")) + ":(" + oldStr + ") => (" + newStr + ");");
modInfo += mapField.get(mp.get("name")) + ":(" + oldStr + ") => (" + newStr + ");";
} else {
modInfo += mapField.get(mp.get("name")) + ":(" + mp.get("old") + ") => (" + mp.get("new") + ");";
}
} return modInfo;
}

函数传入的参数是两个对象差异的属性,在循环进行解析并进行字符串拼接后,就可以获得对应的日志了。

调用方法

在 JeeSite 中提交数据后,无论是修改还是新建,都会调用相关 Controller 中的 save 方法,因此上面的方法需要在 save 方法中进行调用。

相同的方法如何判断当前是新建,还是修改呢?判断的方法就是判断传入的对象中是否有 id,如果有 id 则说明是修改,如果没有 id 则说明是新建。

具体代码如下:

 /*
* 如果id不为空,则表示为修改
*/
if ( StringUtils.isNotBlank(newXxx.getId()) ) {
Xxx oldXxx = new Xxx();
// 获取原来的信息
oldXxx = xxxService.get(newXxx.getId()); // 比较修改后的信息和未修改的信息
List<Map<String, Object>> modList = compareTwoClass(oldXxx, newXxx);
// 生成差异信息
String strModifyInfo = catModifyInfo(modList);
// 输出差异字符串
System.out.println(strModifyInfo); // 把修改记录保存到日志表中
// ...
}

有了以上的方式就可以实现修改信息前后的日志记录了,修改后的情况如下:

不过该方式并不完美,如果修改了表字段的名称或数量,那么代码也要相应的修改,如果新添加的字段有对应的字典,那么也要添加字典对应的关联,这样就需要每次修改代码,十分的不方便了。

解决的方式很简单,使用 JeeSite 中代码生成的功能,就可以解决该问题。


我的微信公众号:“码农UP2U”

JeeSite | 保存信息修改记录的更多相关文章

  1. JeeSite | 保存信息修改记录封装

    前面写过两篇关于“保存信息修改记录”的内容,分别如下: JeeSite | 保存信息修改记录 JeeSite | 保存信息修改记录续 回顾         第一篇文章通过类字段的比较返回一个有字段值不 ...

  2. JeeSite | 保存信息修改记录续

    遗留问题 上篇文章中遗留了一个问题,就是为了要关联类属性与注释,注释与字典的地方使用了两个map来逐个添加了相关的信息,如下所示: Map<String, String> mapField ...

  3. ASP.NET保存信息总结(Application、Session、Cookie、ViewState和Cache等) ZT

    http://www.cnblogs.com/ranran/p/4065619.html http://www.cnblogs.com/jxlsomnus/p/4450911.html 以下是关于AS ...

  4. [ASP.net教程]ASP.NET保存信息总结(Application、Session、Cookie、ViewState和Cache等)

    以下是关于ASP.NET中保存各种信息的对象的比较,理解这些对象的原理,对制作完善的程序来说是相当有必要的(摘至互联网,并非原创--xukunping)在ASP.NET中,有很多种保存信息的对象.例如 ...

  5. 玩转web之servlet(六)---session介绍及简单使用(登录验证中保存信息)

    在浏览器与服务器进行交互时,往往需要把涉及到的一些数据保存下来,这时就需要使用cookie或session进行状态管理. 这篇文章先来说说session怎么用,首先在servlet中创建一个sessi ...

  6. ASP.NET保存信息总结(Application、Session、Cookie、ViewState和Cache等)

    以下是关于ASP.NET中保存各种信息的对象的比较,理解这些对象的原理,对制作完善的程序来说是相当有必要的(摘至互联网,并非原创--xukunping) 在ASP.NET中,有很多种保存信息的对象.例 ...

  7. C#实现在注册表中保存信息

    C#实现在注册表中保存信息 最近做的项目需要在注册表中记录一些用户设置,方便在程序下次启动时读取设置,应用上次用户保存的设置,挺简单的. 写出来,方便记忆,以后要用,可以直接改改就能用. 1 usin ...

  8. python中利用类创建的对象来保存信息

    在类创建的对象中,一般都是以字典的方式来保存信息 class Student: def __init__(self, name, age, score): self.name = name self. ...

  9. 3.8学习总结——Android保存信息

    为了保存软件的设置参数,Android平台为我们提供了一个SharedPreferences接口,它是一个轻量级的存储类,特别适合用于保存软件配置参数.使用SharedPreferences保存数据, ...

随机推荐

  1. centos7.6 jumpserver 堡垒机 重启启动顺序

    cd /sdata/usr/local python3. -m venv py3 source /sdata/usr/local/py3/bin/activate cd /sdata/usr/loca ...

  2. Markdown语法教程

    标题 # 一级标题 ## 二级标题 ### 三级标题 #### 四级标题 ##### 五级标题 ###### 六级标题 效果如下: 一级标题 二级标题 三级标题 四级标题 五级标题 六级标题 段落 换 ...

  3. Linux系统学习 十八、VSFTP服务—虚拟用户访问—配置虚拟用户访问

    配置虚拟用户访问 首先至少要关闭userlist 改完配置文件是要重启服务来使它生效 其实在刚装好vsftp的时候的配置文件不用修改的情况下配置虚拟用户访问控制是最好的 local_root选项不影响 ...

  4. pytest系列(一):什么是单元测试界的高富帅?

    pytest是python语言中一款强大的单元测试框架,用来管理和组织测试用例,可应用在单元测试.自动化测试工作中. unittest也是python语言中一款单元测试框架,但是功能有限,没有pyte ...

  5. ism 发布

    ism 发布 ism 发布 ism 发布

  6. 【转】开发一个这样的 APP 要多长时间?

    作者:蒋国刚 www.cnblogs.com/guogangj/p/4676836.html 呵呵. 这是一个“如有雷同,纯属巧合”的故事,外加一些废话,大家请勿对号入座.开始了…… 我有些尴尬地拿着 ...

  7. ASP.NET Core on K8S深入学习(6)Health Check

    本篇已加入<.NET Core on K8S学习实践系列文章索引>,可以点击查看更多容器化技术相关系列文章. 一.关于K8S中的Health Check 所谓Health Check,就是 ...

  8. 1+x 证书 Web 前端开发中级理论考试(试卷 8 )含答案

    1+x 证书 Web 前端开发中级理论考试(试卷 8 ) 官方QQ群 转载请注明来源:妙笔生花个人博客http://blog.zh66.club/index.php/archives/438/ 一.单 ...

  9. 【docker构建】基于docker搭建redis数据库缓存服务

    搭建redis服务 首先拉取镜像==>运行镜像为容器 # 先搜索下redis镜像 [root@VM_0_10_centos wordpress]# docker search redis # 拉 ...

  10. ASP.NET Core Web 应用程序系列(二)- 在ASP.NET Core中使用Autofac替换自带DI进行批量依赖注入(MVC当中应用)

    在上一章中主要和大家分享在MVC当中如何使用ASP.NET Core内置的DI进行批量依赖注入,本章将继续和大家分享在ASP.NET Core中如何使用Autofac替换自带DI进行批量依赖注入. P ...