需求點 在很多場景中信息是不能輕易被修改的,修改時要麼需要具備許可權,要麼需要審批,但是無論是哪種方式,修改前後的數據都是需要留有“案底”的,也就是說關鍵的信息被修改後是有修改記錄的,一般修改記錄會記錄修改人、修改日期和修改的數據欄位。 比如,修改一個人的姓名從“張三”變為了“李四”,那麼在進行記錄的 ...
需求點
在很多場景中信息是不能輕易被修改的,修改時要麼需要具備許可權,要麼需要審批,但是無論是哪種方式,修改前後的數據都是需要留有“案底”的,也就是說關鍵的信息被修改後是有修改記錄的,一般修改記錄會記錄修改人、修改日期和修改的數據欄位。
比如,修改一個人的姓名從“張三”變為了“李四”,那麼在進行記錄的時候,記錄的信息可能如下:
姓名:(張三)=>(李四);性別:(女)=>(男);
這樣就很好的體現出了修改了哪個欄位,修改前後的數據分別是什麼。關鍵的信息無論怎麼修改都會有據可查,時間、人物、修改數據前後信息等。
判斷被修改的數據
在頁面中將數據修改並提交頁面後,數據會從 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”