SpringCloud或SpringBoot+Mybatis-Plus利用mybatis插件實現數據操做記錄及更新對比

引文html

  本文主要介紹如何使用mybatis插件實現攔截數據庫操做並根據不一樣需求進行數據對比分析,主要適用於系統中須要對數據操做進行記錄、在更新數據時準確記錄更新字段java

核心:mybatis插件(攔截器)、mybatis-Plus實體規範、數據對比spring

 

一、相關技術簡介

mybatis插件sql

  mybatis插件實際上就是官方針對4層數據操做處理預留的攔截器,使用者能夠根據不一樣的需求進行操做攔截並處理。這邊筆者不作詳細描述,詳細介紹請到官網瞭解,這裏筆者就複用官網介紹。數據庫

插件(plugins)

MyBatis 容許你在已映射語句執行過程當中的某一點進行攔截調用。默認狀況下,MyBatis 容許使用插件來攔截的方法調用包括:apache

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

這些類中方法的細節能夠經過查看每一個方法的簽名來發現,或者直接查看 MyBatis 發行包中的源代碼。 若是你想作的不只僅是監控方法的調用,那麼你最好至關了解要重寫的方法的行爲。 由於若是在試圖修改或重寫已有方法的行爲的時候,你極可能在破壞 MyBatis 的核心模塊。 這些都是更低層的類和方法,因此使用插件的時候要特別小心。api

經過 MyBatis 提供的強大機制,使用插件是很是簡單的,只需實現 Interceptor 接口,並指定想要攔截的方法簽名便可。緩存

 1 // ExamplePlugin.java
 2 @Intercepts({@Signature(
 3   type= Executor.class,
 4   method = "update",
 5   args = {MappedStatement.class,Object.class})})
 6 public class ExamplePlugin implements Interceptor {
 7   private Properties properties = new Properties();
 8   public Object intercept(Invocation invocation) throws Throwable {
 9     // implement pre processing if need
10     Object returnObject = invocation.proceed();
11     // implement post processing if need
12     return returnObject;
13   }
14   public void setProperties(Properties properties) {
15     this.properties = properties;
16   }
17 }
18 <!-- mybatis-config.xml -->
19 <plugins>
20   <plugin interceptor="org.mybatis.example.ExamplePlugin">
21     <property name="someProperty" value="100"/>
22   </plugin>
23 </plugins>

上面的插件將會攔截在 Executor 實例中全部的 「update」 方法調用, 這裏的 Executor 是負責執行低層映射語句的內部對象。mybatis

提示 覆蓋配置類app

除了用插件來修改 MyBatis 核心行爲以外,還能夠經過徹底覆蓋配置類來達到目的。只需繼承後覆蓋其中的每一個方法,再把它傳遞到 SqlSessionFactoryBuilder.build(myConfig) 方法便可。再次重申,這可能會嚴重影響 MyBatis 的行爲,務請慎之又慎。

 

重點講下4層處理,MyBatis兩級緩存就是在其中兩層中實現

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 
    • 全部數據庫操做到達底層後都由該執行器進行任務分發,主要有update(插入、更新、刪除),query(查詢),提交,回滾,關閉連接等
  • ParameterHandler (getParameterObject, setParameters) 
    • 參數處理器(獲取參數,設置參數)
  • ResultSetHandler (handleResultSets, handleOutputParameters) 
    • 結果集處理器(結果集,輸出參數)
  • StatementHandler (prepare, parameterize, batch, update, query) 
    • 聲明處理器、準備連接jdbc前處理,prepare(預處理):生成sql語句,準備連接數據庫進行操做

以上4層執行順序爲順序執行

  • Executor是 Mybatis的內部執行器,它負責調用StatementHandler操做數據庫,並把結果集經過 ResultSetHandler進行自動映射,另外,他還處理了二級緩存的操做。從這裏能夠看出,咱們也是能夠經過插件來實現自定義的二級緩存的。
  • ParameterHandler是Mybatis實現Sql入參設置的對象。插件能夠改變咱們Sql的參數默認設置。
  • ResultSetHandler是Mybatis把ResultSet集合映射成POJO的接口對象。咱們能夠定義插件對Mybatis的結果集自動映射進行修改。
  • StatementHandler是Mybatis直接和數據庫執行sql腳本的對象。另外它也實現了Mybatis的一級緩存。這裏,咱們可使用插件來實現對一級緩存的操做(禁用等等)。

MyBatis-Plus:

  MyBatis加強器,主要規範了數據實體,在底層實現了簡單的增刪查改,使用者再也不須要開發基礎操做接口,小編認爲是最強大、最方便易用的,沒有之一,不接受任何反駁。詳細介紹請看官網

數據實體的規範讓底層操做更加便捷,本例主要實體規範中的表名以及主鍵獲取,下面上實體規範demo

 1 @Data
 2 @TableName("tb_demo")
 3 @EqualsAndHashCode(callSuper = true)
 4 public class Demo extends Model<Demo> {
 5 private static final long serialVersionUID = 1L;
 6 
 7     /**
 8    * 
 9    */
10     @TableId
11     private Integer id;
12     /**
13    * 名稱
14    */
15     private String name;
16 
17 }
View Code
 

二、實現

本文所要講述的就是在第一級(Executor)進行攔截並實現數據對比記錄。
本例爲公共模塊實現,而後在其它模塊中依賴此公共模塊,根據每一個模塊不一樣的需求自定義實現不一樣的處理。
結構目錄

 

1、實現攔截器

DataUpdateInterceptor,根據官網demo實現攔截器,在攔截器中根據增、刪、改操做去調用各個模塊中自定義實現的處理方法來達到不一樣的操做處理。

  1 package com.erp4cloud.rerp.common.data.log;
  2 
  3 import com.baomidou.mybatisplus.annotation.TableId;
  4 import com.baomidou.mybatisplus.annotation.TableName;
  5 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  6 import com.baomidou.mybatisplus.extension.activerecord.Model;
  7 import lombok.AllArgsConstructor;
  8 import org.apache.commons.lang.StringUtils;
  9 import org.apache.ibatis.executor.Executor;
 10 import org.apache.ibatis.mapping.MappedStatement;
 11 import org.apache.ibatis.mapping.SqlCommandType;
 12 import org.apache.ibatis.plugin.*;
 13 import org.springframework.scheduling.annotation.Async;
 14 
 15 import javax.sql.DataSource;
 16 import java.lang.reflect.Field;
 17 import java.lang.reflect.InvocationTargetException;
 18 import java.util.Collection;
 19 import java.util.List;
 20 import java.util.Map;
 21 import java.util.Properties;
 22 
 23 /**
 24  * 數據更新攔截器
 25  *
 26  * @author Tophua
 27  * @date 2019/8/2
 28  */
 29 @AllArgsConstructor
 30 @Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
 31 public class DataUpdateInterceptor implements Interceptor {
 32 
 33     private final DataSource dataSource;
 34     private final DataLogHandler dataLogHandler;
 35 
 36     @Override
 37     public Object intercept(Invocation invocation) {
 38         Object result = null;
 39         try {
 40             this.dealData(invocation);
 41             result = invocation.proceed();
 42         } catch (InvocationTargetException e) {
 43             e.printStackTrace();
 44         } catch (IllegalAccessException e) {
 45             e.printStackTrace();
 46         }
 47         return result;
 48     }
 49 
 50     @Override
 51     public Object plugin(Object target) {
 52         if (target instanceof Executor) {
 53             return Plugin.wrap(target, this);
 54         }
 55         return target;
 56     }
 57 
 58     @Override
 59     public void setProperties(Properties properties) {
 60 
 61     }
 62 
 63     /**
 64      * 對數據庫操做傳入參數進行處理
 65      *
 66      * @param invocation
 67      * @return void
 68      * @author Tophua
 69      * @date 2019/8/3
 70      */
 71     public void dealData(Invocation invocation) {
 72         Object[] args = invocation.getArgs();
 73         MappedStatement mappedStatement = (MappedStatement) args[0];
 74         // 參數
 75         Object et = args[1];
 76         if (et instanceof Model) {
 77             this.doLog(mappedStatement, et);
 78         } else if (et instanceof Map) {
 79             String key = "et";
 80             String listKey = "collection";
 81             if (((Map) et).containsKey(key) && ((Map) et).get(key) instanceof Model) {
 82                 this.doLog(mappedStatement, ((Map) et).get(key));
 83             } else if (((Map) et).containsKey(listKey) && ((Map) et).get(listKey) instanceof Collection) {
 84                 List<Object> list = (List<Object>) ((Map) et).get(listKey);
 85                 for (Object obj : list) {
 86                     if (obj instanceof Model) {
 87                         this.doLog(mappedStatement, obj);
 88                     }
 89                 }
 90             }
 91         }
 92     }
 93 
 94     /**
 95      * 根據不一樣參數及操做進行不一樣的日誌記錄
 96      *
 97      * @param mappedStatement
 98      * @param et
 99      * @return void
100      * @author Tophua
101      * @date 2019/8/3
102      */
103     public void doLog(MappedStatement mappedStatement, Object et) {
104         // 反射獲取實體類
105         Class<?> clazz = et.getClass();
106         // 不含有表名的實體就默認經過
107         if (!clazz.isAnnotationPresent(TableName.class)) {
108             return;
109         }
110         // 獲取表名
111         TableName tableName = clazz.getAnnotation(TableName.class);
112         String tbName = tableName.value();
113         if (StringUtils.isBlank(tbName)) {
114             return;
115         }
116         String pkName = null;
117         String pkValue = null;
118         // 獲取實體全部字段
119         Field[] fields = clazz.getDeclaredFields();
120         for (Field field : fields) {
121             // 設置些屬性是能夠訪問的
122             field.setAccessible(true);
123             if (field.isAnnotationPresent(TableId.class)) {
124                 // 獲取主鍵
125                 pkName = field.getName();
126                 try {
127                     // 獲取主鍵值
128                     pkValue = field.get(et).toString();
129                 } catch (Exception e) {
130                     pkValue = null;
131                 }
132 
133             }
134         }
135         BasicInfo basicInfo = new BasicInfo(dataSource, (Model) et, tbName, pkName, pkValue);
136 
137         // 插入
138         if (SqlCommandType.INSERT.equals(mappedStatement.getSqlCommandType())) {
139             InsertInfo insertInfo = new InsertInfo(basicInfo, et);
140             dataLogHandler.insertHandler(insertInfo);
141         }
142         // 更新
143         if (SqlCommandType.UPDATE.equals(mappedStatement.getSqlCommandType()) && StringUtils.isNotBlank(pkName) && StringUtils.isNotBlank(pkValue)) {
144             Object oldObj = this.queryData(pkName, pkValue, (Model) et);
145             if (oldObj != null) {
146                 UpdateInfo updateInfo = new UpdateInfo(basicInfo, oldObj, et);
147                 // 調用自定義處理方法
148                 dataLogHandler.updateHandler(updateInfo);
149             }
150         }
151         // 刪除
152         if (SqlCommandType.DELETE.equals(mappedStatement.getSqlCommandType()) && StringUtils.isNotBlank(pkName) && StringUtils.isNotBlank(pkValue)) {
153             Object delObj = this.queryData(pkName, pkValue, (Model) et);
154             if (delObj != null) {
155                 DeleteInfo deleteInfo = new DeleteInfo(basicInfo, delObj);
156                 // 調用自定義處理方法
157                 dataLogHandler.deleteHandler(deleteInfo);
158             }
159         }
160     }
161 
162     /**
163      * 根據主鍵和主鍵值查詢數據
164      *
165      * @param pkName
166      * @param pkValue
167      * @param clazz
168      * @return java.lang.Object
169      * @author Tophua
170      * @date 2019/8/5
171      */
172     private Object queryData(String pkName, String pkValue, Model clazz) {
173         // 查詢更新前數據
174         return clazz.selectOne(Wrappers.query().eq(pkName, pkValue));
175     }
176 }
View Code

2、配置

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 import lombok.AllArgsConstructor;
 4 import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
 5 import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
 6 import org.springframework.context.annotation.Bean;
 7 import org.springframework.context.annotation.Configuration;
 8 
 9 import javax.sql.DataSource;
10 
11 /**
12  * 數據更新日誌處理配置(實現按需加載)
13  *
14  * @author Tophua
15  * @date 2019/8/2
16  */
17 @Configuration
18 @AllArgsConstructor
19 @ConditionalOnBean({DataSource.class, DataLogHandler.class})
20 public class DataLogConfig {
21 
22     private final DataLogHandler dataLogHandler;
23     private final DataSource dataSource;
24 
25     @Bean
26     @ConditionalOnMissingBean
27     public DataUpdateInterceptor dataUpdateInterceptor() {
28         return new DataUpdateInterceptor(dataSource, dataLogHandler);
29     }
30 }
View Code

提示:公共模塊中須要在spring.factories(src/main/resources/META-INF/)中進行配置讓Spring自動進行裝配,小筆使用以下

1 org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2     com.erp4cloud.rerp.common.data.log.DataLogConfig

3、在其它模塊實現自定義處理接口

在接口中會根據不一樣操做傳入不一樣參數,各位能夠根據具體方法取出數據進行所需操做。

本例僅測試使用,未實現具體操做,在具體使用中請自行編寫具體邏輯。

DataLogHandler 自定義處理接口,各模塊實現,注意須要將實現類做爲ServiceBean使用,不然該功能沒法生效。

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 /**
 4  * 數據日誌處理
 5  *
 6  * @author Tophua
 7  * @date 2019/8/2
 8  */
 9 public interface DataLogHandler {
10 
11     /**
12      * 插入處理
13      *
14      * @param insertInfo 插入數據信息
15      * @return void
16      * @author Tophua
17      * @date 2019/8/2
18      */
19     void insertHandler(InsertInfo insertInfo);
20 
21     /**
22      * 更新處理
23      *
24      * @param updateInfo 更新數據信息
25      * @return void
26      * @author Tophua
27      * @date 2019/8/2
28      */
29     void updateHandler(UpdateInfo updateInfo);
30 
31     /**
32      * 刪除處理
33      *
34      * @param deleteInfo 刪除數據信息
35      * @return void
36      * @author Tophua
37      * @date 2019/8/3
38      */
39     void deleteHandler(DeleteInfo deleteInfo);
40 }
View Code

實現demo

 1 package com.erp4cloud.rerp.building.log;
 2 
 3 import com.erp4cloud.rerp.common.data.log.*;
 4 import com.fasterxml.jackson.databind.ObjectMapper;
 5 import lombok.AllArgsConstructor;
 6 import lombok.SneakyThrows;
 7 import org.springframework.stereotype.Service;
 8 
 9 import java.util.List;
10 
11 /**
12  * describe
13  *
14  * @author Tophua
15  * @date 2019/8/3
16  */
17 @Service
18 @AllArgsConstructor
19 public class DataLogDeal implements DataLogHandler {
20 
21     ObjectMapper objectMapper = new ObjectMapper();
22 
23     @SneakyThrows
24     @Override
25     public void insertHandler(InsertInfo insertInfo) {
26         System.out.println("插入:" + objectMapper.writeValueAsString(insertInfo.getInsertObj()));
27     }
28 
29     @SneakyThrows
30     @Override
31     public void updateHandler(UpdateInfo updateInfo) {
32         List<CompareResult> cr = updateInfo.getCompareResult();
33         StringBuilder sb = new StringBuilder();
34         sb.append("更新\"");
35         sb.append(updateInfo.getBasicInfo().getTbName());
36         sb.append("\" 表 ");
37         cr.forEach(r -> {
38             String s = "把《" + r.getFieldComment() + "》從<" + r.getOldValue() + ">改爲<" + r.getNewValue() + ">";
39             sb.append(s);
40         });
41         System.out.println(sb.toString());
42     }
43 
44     @SneakyThrows
45     @Override
46     public void deleteHandler(DeleteInfo deleteInfo) {
47         System.out.println("刪除:" + objectMapper.writeValueAsString(deleteInfo.getDeleteObj()));
48     }
49 }
View Code

4、其它代碼

BaseDataLogHandler 基礎處理抽象類,提供底層數據對比方法。

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 import lombok.AllArgsConstructor;
 4 import lombok.Getter;
 5 
 6 import java.lang.reflect.Field;
 7 import java.util.ArrayList;
 8 import java.util.List;
 9 import java.util.Optional;
10 
11 /**
12  * 數據日誌基礎信息及處理
13  *
14  * @author Tophua
15  * @date 2019/8/5
16  */
17 @Getter
18 @AllArgsConstructor
19 public abstract class BaseDataLogHandler {
20 
21     /**
22      * 數據基礎信息
23      */
24     private BasicInfo basicInfo;
25 
26     /**
27      * 對比兩個對象
28      *
29      * @param oldObj 舊對象
30      * @param newObj 新對象
31      * @return java.util.List<com.erp4cloud.rerp.common.data.log.CompareResult>
32      * @author Tophua
33      * @date 2019/8/5
34      */
35     protected List<CompareResult> compareTowObject(Object oldObj, Object newObj) throws IllegalAccessException {
36         List<CompareResult> list = new ArrayList<>();
37         //獲取對象的class
38         Class<?> clazz1 = oldObj.getClass();
39         Class<?> clazz2 = newObj.getClass();
40         //獲取對象的屬性列表
41         Field[] field1 = clazz1.getDeclaredFields();
42         Field[] field2 = clazz2.getDeclaredFields();
43         //遍歷屬性列表field1
44         for (int i = 0; i < field1.length; i++) {
45             //遍歷屬性列表field2
46             for (int j = 0; j < field2.length; j++) {
47                 //若是field1[i]屬性名與field2[j]屬性名內容相同
48                 if (field1[i].getName().equals(field2[j].getName())) {
49                     field1[i].setAccessible(true);
50                     field2[j].setAccessible(true);
51                     if (field2[j].get(newObj) == null) {
52                         continue;
53                     }
54                     //若是field1[i]屬性值與field2[j]屬性值內容不相同
55                     if (!compareTwo(field1[i].get(oldObj), field2[j].get(newObj))) {
56                         CompareResult r = new CompareResult();
57                         r.setFieldName(field1[i].getName());
58                         r.setOldValue(field1[i].get(oldObj));
59                         r.setNewValue(field2[j].get(newObj));
60 
61                         // 匹配字段註釋
62                         Optional o = this.basicInfo.getFieldInfos().stream()
63                                 .filter(f -> r.getFieldName().equals(f.getJFieldName())).findFirst();
64                         if (o.isPresent()) {
65                             r.setFieldComment(((FieldInfo) o.get()).getComment());
66                         }
67                         list.add(r);
68                     }
69                     break;
70                 }
71             }
72         }
73         return list;
74     }
75 
76     /**
77      * 對比兩個數據是否內容相同
78      *
79      * @param object1,object2
80      * @return boolean類型
81      */
82     private boolean compareTwo(Object object1, Object object2) {
83 
84         if (object1 == null && object2 == null) {
85             return true;
86         }
87         if (object1 == null && object2 != null) {
88             return false;
89         }
90         if (object1.equals(object2)) {
91             return true;
92         }
93         return false;
94     }
95 
96 }
View Code

BasicInfo 基礎信息,數據源,本表字段信息等。

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 import cn.hutool.db.Db;
 4 import com.baomidou.mybatisplus.extension.activerecord.Model;
 5 import lombok.Getter;
 6 import org.apache.commons.lang.StringUtils;
 7 import org.apache.commons.lang.WordUtils;
 8 
 9 import javax.sql.DataSource;
10 import java.sql.SQLException;
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.concurrent.ConcurrentHashMap;
14 
15 /**
16  * 基礎信息
17  *
18  * @author Tophua
19  * @date 2019/8/5
20  */
21 @Getter
22 public class BasicInfo {
23     private static ConcurrentHashMap<String, List<FieldInfo>> fields = new ConcurrentHashMap<>();
24 
25     /**
26      * 數據源
27      */
28     private DataSource dataSource;
29     /**
30      * mybatis數據底層
31      */
32     private Model model;
33     /**
34      * 表名
35      */
36     private String tbName;
37     /**
38      * 主鍵名稱
39      */
40     private String pkName;
41     /**
42      * 主鍵值
43      */
44     private String pkValue;
45 
46     /**
47      * 表字段註釋
48      */
49     private List<FieldInfo> fieldInfos;
50 
51     public BasicInfo(DataSource dataSource, Model model, String tbName, String pkName, String pkValue) {
52         this.dataSource = dataSource;
53         this.model = model;
54         this.tbName = tbName;
55         this.pkName = pkName;
56         this.pkValue = pkValue;
57     }
58 
59     public List<FieldInfo> getFieldInfos() {
60         if (!fields.containsKey(this.tbName)) {
61             String query = "select column_name fieldName, column_comment comment from information_schema.columns" +
62                     " where table_name = \"" + this.tbName + "\" and table_schema = (select database())";
63             try {
64                 this.fieldInfos = Db.use(dataSource).query(query, FieldInfo.class);
65             } catch (SQLException e) {
66                 this.fieldInfos = new ArrayList<>();
67             }
68             this.fieldInfos.forEach(f -> {
69                 String caseName = this.columnToJava(f.getFieldName());
70                 f.setJFieldName(StringUtils.uncapitalize(caseName));
71             });
72             fields.put(this.tbName, this.fieldInfos);
73         }
74         return fields.get(this.tbName);
75     }
76 
77     /**
78      * 列名轉換成Java屬性名
79      */
80     private String columnToJava(String columnName) {
81         return WordUtils.capitalizeFully(columnName, new char[]{'_'}).replace("_", "");
82     }
83 }
View Code

FieldInfo 字段信息

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 import lombok.Data;
 4 
 5 /**
 6  * 字段信息
 7  *
 8  * @author Tophua
 9  * @date 2019/8/5
10  */
11 @Data
12 public class FieldInfo {
13 
14     /**
15      * 字段名
16      */
17     private String fieldName;
18     /**
19      * java字段名
20      */
21     private String jFieldName;
22     /**
23      * 註釋
24      */
25     private String comment;
26 }
View Code

CompareResult 字段對比結果

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 import lombok.Data;
 4 
 5 /**
 6  * 對比兩個對象結果
 7  *
 8  * @author Tophua
 9  * @date 2019/8/5
10  */
11 @Data
12 public class CompareResult {
13 
14     /**
15      * 字段名
16      */
17     private String fieldName;
18     /**
19      * 字段註釋
20      */
21     private String fieldComment;
22     /**
23      * 字段舊值
24      */
25     private Object oldValue;
26     /**
27      * 字段新值
28      */
29     private Object newValue;
30 }
View Code

InsertInfo 插入信息

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 import lombok.Getter;
 4 
 5 /**
 6  * 數據插入信息
 7  *
 8  * @author Tophua
 9  * @date 2019/8/5
10  */
11 @Getter
12 public class InsertInfo extends BaseDataLogHandler {
13 
14     /**
15      * 插入對象
16      */
17     private Object insertObj;
18 
19     public InsertInfo(BasicInfo basicInfo, Object insertObj) {
20         super(basicInfo);
21         this.insertObj = insertObj;
22     }
23 
24 }
View Code

UpdateInfo 更新信息

 1 package com.erp4cloud.rerp.common.data.log;
 2 
 3 import lombok.Getter;
 4 
 5 import java.util.List;
 6 
 7 /**
 8  * 數據更新信息
 9  *
10  * @author Tophua
11  * @date 2019/8/5
12  */
13 @Getter
14 public class UpdateInfo extends BaseDataLogHandler {
15 
16     /**
17      * 更新前對象
18      */
19     private Object oldObj;
20     /**
21      * 更新對象
22      */
23     private Object newObj;
24 
25     public UpdateInfo(BasicInfo basicInfo, Object oldObj, Object newObj) {
26         super(basicInfo);
27         this.oldObj = oldObj;
28         this.newObj = newObj;
29     }
30 
31     public List<CompareResult> getCompareResult() throws IllegalAccessException {
32         return compareTowObject(this.oldObj, this.newObj);
33     }
34 }
View Code

DeleteInfo 刪除信息

package com.erp4cloud.rerp.common.data.log;

import lombok.Getter;

/**
 * 數據刪除信息
 *
 * @author Tophua
 * @date 2019/8/5
 */
@Getter
public class DeleteInfo extends BaseDataLogHandler {

    /**
     * 刪除對象
     */
    private Object deleteObj;

    public DeleteInfo(BasicInfo basicInfo, Object deleteObj) {
        super(basicInfo);
        this.deleteObj = deleteObj;
    }
}
View Code

 4、結果

更新時控制檯打印:

1 更新"customer_resource_base_info" 表 把《姓名》從<測試>改爲<測試dsffgggg>把《身份證》從<2222222>改爲<3333333333333> 

因爲是測試因此未進行數據庫保存,你們自行保存。

 

三、總結

本例主要解決多實體數據更新先後對比記錄,固然也可以使用AOP實現數據對比,但經筆者實現感受仍是此方法實現起來相對簡單。筆者更推薦使用底層技術直接進行攔截處理,這樣能保證任何數據操做都毫無遺漏,不放過任何操做。

目前本例暫未實現數據無主鍵更新記錄,但業務中常常會出現無主鍵根據其它條件更新,因此本例還可進行優化提高,在此筆者就先放一段了,等後續再進行升級更新。

 

歡迎各位大神交流意見。。。。。。

相關文章
相關標籤/搜索