SpringCloud或SpringBoot+Mybatis-Plus利用mybatis插件实现数据操作记录及更新对比

Posted top-sky-hua

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud或SpringBoot+Mybatis-Plus利用mybatis插件实现数据操作记录及更新对比相关的知识,希望对你有一定的参考价值。

引文

  本文主要介绍如何使用mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录、在更新数据时准确记录更新字段

核心:mybatis插件(拦截器)、mybatis-Plus实体规范、数据对比

 

1、相关技术简介

mybatis插件

  mybatis插件实际上就是官方针对4层数据操作处理预留的拦截器,使用者可以根据不同的需求进行操作拦截并处理。这边笔者不做详细描述,详细介绍请到官网了解,这里笔者就复用官网介绍。

插件(plugins)

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

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

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。

通过 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 核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到 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
 

2、实现

本文所要讲述的就是在第一级(Executor)进行拦截并实现数据对比记录。
本例为公共模块实现,然后在其它模块中依赖此公共模块,根据每个模块不同的需求自定义实现不同的处理。
结构目录
技术图片

 

一、实现拦截器

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

二、配置

技术图片
 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

三、在其它模块实现自定义处理接口

在接口中会根据不同操作传入不同参数,各位可以根据具体方法取出数据进行所需操作。

本例仅测试使用,未实现具体操作,在具体使用中请自行编写具体逻辑。

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

四、其它代码

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

 

3、总结

本例主要解决多实体数据更新前后对比记录,当然也可使用AOP实现数据对比,但经笔者实现感觉还是此方法实现起来相对简单。笔者更推荐使用底层技术直接进行拦截处理,这样能保证任何数据操作都毫无遗漏,不放过任何操作。

目前本例暂未实现数据无主键更新记录,但业务中经常会出现无主键根据其它条件更新,所以本例还可进行优化提升,在此笔者就先放一段了,等后续再进行升级更新。

 

欢迎各位大神交流意见。。。。。。

 

以上是关于SpringCloud或SpringBoot+Mybatis-Plus利用mybatis插件实现数据操作记录及更新对比的主要内容,如果未能解决你的问题,请参考以下文章

springboot+springcloud相关面试题

springboot+springcloud相关面试题

SpringBoot和SpringCloud面试题

SpringCloud或SpringBoot+Mybatis-Plus利用mybatis插件实现数据操作记录及更新对比

企业分布式微服务云SpringCloud SpringBoot mybatis (十七)Spring Boot中的事务管理

微服务架构SpringBoot+SpringCloud+VUE一 || 微服务简介