JeeSite | 保存信息修改记录
需求点
在很多场景中信息是不能轻易被修改的,修改时要么需要具备权限,要么需要审批,但是无论是哪种方式,修改前后的数据都是需要留有“案底”的,也就是说关键的信息被修改后是有修改记录的,一般修改记录会记录修改人、修改日期和修改的数据字段。
比如,修改一个人的姓名从“张三”变为了“李四”,那么在进行记录的时候,记录的信息可能如下:
姓名:(张三)=>(李四);性别:(女)=>(男);
这样就很好的体现出了修改了哪个字段,修改前后的数据分别是什么。关键的信息无论怎么修改都会有据可查,时间、人物、修改数据前后信息等。
判断被修改的数据
在页面中将数据修改并提交页面后,数据会从 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 | 保存信息修改记录的更多相关文章
- JeeSite | 保存信息修改记录封装
前面写过两篇关于“保存信息修改记录”的内容,分别如下: JeeSite | 保存信息修改记录 JeeSite | 保存信息修改记录续 回顾 第一篇文章通过类字段的比较返回一个有字段值不 ...
- JeeSite | 保存信息修改记录续
遗留问题 上篇文章中遗留了一个问题,就是为了要关联类属性与注释,注释与字典的地方使用了两个map来逐个添加了相关的信息,如下所示: Map<String, String> mapField ...
- 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 ...
- [ASP.net教程]ASP.NET保存信息总结(Application、Session、Cookie、ViewState和Cache等)
以下是关于ASP.NET中保存各种信息的对象的比较,理解这些对象的原理,对制作完善的程序来说是相当有必要的(摘至互联网,并非原创--xukunping)在ASP.NET中,有很多种保存信息的对象.例如 ...
- 玩转web之servlet(六)---session介绍及简单使用(登录验证中保存信息)
在浏览器与服务器进行交互时,往往需要把涉及到的一些数据保存下来,这时就需要使用cookie或session进行状态管理. 这篇文章先来说说session怎么用,首先在servlet中创建一个sessi ...
- ASP.NET保存信息总结(Application、Session、Cookie、ViewState和Cache等)
以下是关于ASP.NET中保存各种信息的对象的比较,理解这些对象的原理,对制作完善的程序来说是相当有必要的(摘至互联网,并非原创--xukunping) 在ASP.NET中,有很多种保存信息的对象.例 ...
- C#实现在注册表中保存信息
C#实现在注册表中保存信息 最近做的项目需要在注册表中记录一些用户设置,方便在程序下次启动时读取设置,应用上次用户保存的设置,挺简单的. 写出来,方便记忆,以后要用,可以直接改改就能用. 1 usin ...
- python中利用类创建的对象来保存信息
在类创建的对象中,一般都是以字典的方式来保存信息 class Student: def __init__(self, name, age, score): self.name = name self. ...
- 3.8学习总结——Android保存信息
为了保存软件的设置参数,Android平台为我们提供了一个SharedPreferences接口,它是一个轻量级的存储类,特别适合用于保存软件配置参数.使用SharedPreferences保存数据, ...
随机推荐
- centos7 配置阿里镜像
1. 备份原来的yum源 cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bak 2.设置aliyun的y ...
- RabbitMQ基础理解
RabbitMQ基本理解 MQ是消息中间件,常见的有RabbitMQ,Kafka,RocketMQ,activeMQ 等,用于分布式系统中.作用有三点 解耦 异步 削峰 RabbitMQ 整体上是一个 ...
- Python数据结构性能分析
1.目标 告诉大家Python列表和字典操作的 大O 性能.然后我们将做一些基于时间的实验来说明每个数据结构的花销和使用这些数据结构的好处 2.实操 在列表的操作有一个非常常见的编程任务就是是增加一个 ...
- vue使用--vuex快速学习与使用
什么是vuex? Vuex核心概念 Vuex安装与使用 1.安装 2.目录结构与vuex引入 3.store中变量的定义.管理.派生(getter) 4.vuex辅助函数的使用说明 Vuex刷新数据丢 ...
- FFT/NTT中档题总结
被DeepinC%怕了,把一些题放到这里来 T1Normal 其实这道题放到中档题也不太合适,个人感觉真的很难,机房里好像都是颓的题解 因为期望的可加性,把每个点的贡献单独处理,即求期望深度 考虑$y ...
- 使用Castle DynamicProxy (AOP)
在本文中,我将引导您了解.NET环境中的面向方面编程(AOP)概念,以及如何使用Castle DynamicProxy创建和附加方面.在我们开始之前,让我快速介绍AOP和 IoC.如果您已经熟悉这些 ...
- Mybatis基本类型参数非空判断(异常:There is no getter for property...)
先看一小段代码 <select id="queryByPhone" parameterType="java.lang.String" resultType ...
- Web安全测试学习笔记-DVWA-登录密码爆破(使用Burp Suite)
密码爆破简单来说,就是使用密码本(记录了若干密码),用工具(手工也可以,if you like...)一条条读取密码本中的密码后发送登录请求,遍历密码本的过程中可能试出真正的密码. 本文学习在已知登录 ...
- docker 部署springcloud项目
一.首先从 docker.hub[地址:https://hub.docker.com] 中根据各自的需求 pull 对应的 openjdk镜像(本次直接在修改后的docker镜像源中下载) docke ...
- QLineEdit限制数据类型——只能输入浮点型数
前言 最近做了一个小的上位机,要通过串口来下发几个时间参数,为了防止误输入,产生不必要的麻烦,我把输入范围限制在0-680的浮点型数据,支持小数点后2位.学习了一下QLineEdit类是如何限制输入类 ...