|
@@ -0,0 +1,296 @@
|
|
|
+package com.chelvc.framework.database.support;
|
|
|
+
|
|
|
+import java.io.Serializable;
|
|
|
+import java.lang.reflect.Field;
|
|
|
+import java.util.Collections;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Objects;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
|
|
|
+import com.baomidou.mybatisplus.core.metadata.TableInfo;
|
|
|
+import com.chelvc.framework.common.util.JacksonUtils;
|
|
|
+import com.chelvc.framework.common.util.ObjectUtils;
|
|
|
+import com.chelvc.framework.database.context.DatabaseContextHolder;
|
|
|
+import com.google.common.collect.Maps;
|
|
|
+import lombok.AllArgsConstructor;
|
|
|
+import lombok.Data;
|
|
|
+import lombok.NoArgsConstructor;
|
|
|
+import lombok.NonNull;
|
|
|
+import lombok.experimental.SuperBuilder;
|
|
|
+
|
|
|
+/**
|
|
|
+ * binlog日志
|
|
|
+ *
|
|
|
+ * @author Woody
|
|
|
+ * @date 2024/10/15
|
|
|
+ */
|
|
|
+@Data
|
|
|
+@SuperBuilder
|
|
|
+@NoArgsConstructor
|
|
|
+@AllArgsConstructor
|
|
|
+public class Binlog implements Serializable {
|
|
|
+ /**
|
|
|
+ * 操作类型(c -> 新增、u -> 修改、 d -> 删除)
|
|
|
+ */
|
|
|
+ private String op;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新前字段/值映射表
|
|
|
+ */
|
|
|
+ private Map<String, Object> before;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新后字段/值映射表
|
|
|
+ */
|
|
|
+ private Map<String, Object> after;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换字段值
|
|
|
+ *
|
|
|
+ * @param field 字段对象
|
|
|
+ * @param object 字段原始值
|
|
|
+ * @return 转换后字段值
|
|
|
+ */
|
|
|
+ public static Object convert(@NonNull Field field, Object object) {
|
|
|
+ Class<?> type = field.getType();
|
|
|
+ if (object instanceof String && (Map.class.isAssignableFrom(type) || List.class.isAssignableFrom(type))) {
|
|
|
+ // binlog消息不支持JSON格式,所以需要单独反序列化处理
|
|
|
+ return JacksonUtils.deserialize((String) object, field.getGenericType());
|
|
|
+ }
|
|
|
+ return JacksonUtils.convert(object, type);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换实体对象实例
|
|
|
+ *
|
|
|
+ * @param model 实体模型
|
|
|
+ * @param mapping 字段名/值映射表
|
|
|
+ * @param <T> 实体类型
|
|
|
+ * @return 实体对象实例
|
|
|
+ */
|
|
|
+ public static <T> T convert(@NonNull Class<T> model, Map<?, ?> mapping) {
|
|
|
+ T entity;
|
|
|
+ try {
|
|
|
+ entity = model.newInstance();
|
|
|
+ } catch (InstantiationException | IllegalAccessException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ TableInfo table = DatabaseContextHolder.getTable(model);
|
|
|
+ update(table, entity, mapping);
|
|
|
+ return entity;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新实体属性值
|
|
|
+ *
|
|
|
+ * @param entity 实体对象实例
|
|
|
+ * @param mapping 字段名/值映射表
|
|
|
+ */
|
|
|
+ public static void update(@NonNull Object entity, Map<?, ?> mapping) {
|
|
|
+ Class<?> model = entity.getClass();
|
|
|
+ TableInfo table = DatabaseContextHolder.getTable(model);
|
|
|
+ update(table, entity, mapping);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新实体属性值
|
|
|
+ *
|
|
|
+ * @param table 表信息
|
|
|
+ * @param entity 实体对象实例
|
|
|
+ * @param mapping 字段名/值映射表
|
|
|
+ */
|
|
|
+ private static void update(TableInfo table, Object entity, Map<?, ?> mapping) {
|
|
|
+ if (ObjectUtils.isEmpty(mapping)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新主键字段值
|
|
|
+ if (table.havePK()) {
|
|
|
+ Object value = mapping.get(table.getKeyColumn());
|
|
|
+ value = JacksonUtils.convert(value, table.getKeyType());
|
|
|
+ ObjectUtils.setValue(entity, table.getKeyProperty(), value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新普通字段值
|
|
|
+ for (TableFieldInfo field : table.getFieldList()) {
|
|
|
+ Object value = mapping.get(field.getColumn());
|
|
|
+ value = convert(field.getField(), value);
|
|
|
+ ObjectUtils.setValue(entity, field.getField(), value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是否新增
|
|
|
+ *
|
|
|
+ * @return true/false
|
|
|
+ */
|
|
|
+ public boolean isInsert() {
|
|
|
+ return Objects.equals(this.op, "c");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是否修改
|
|
|
+ *
|
|
|
+ * @return true/false
|
|
|
+ */
|
|
|
+ public boolean isUpdate() {
|
|
|
+ return Objects.equals(this.op, "u");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 是否删除
|
|
|
+ *
|
|
|
+ * @return true/false
|
|
|
+ */
|
|
|
+ public boolean isDelete() {
|
|
|
+ return Objects.equals(this.op, "d");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断数据是否更新
|
|
|
+ *
|
|
|
+ * @param model 数据模型
|
|
|
+ * @return true/false
|
|
|
+ */
|
|
|
+ public boolean isModified(@NonNull Class<?> model) {
|
|
|
+ if (ObjectUtils.isEmpty(this.after)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ TableInfo table = DatabaseContextHolder.getTable(model);
|
|
|
+ List<TableFieldInfo> fields = table.getFieldList();
|
|
|
+ if (ObjectUtils.isEmpty(fields)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for (TableFieldInfo field : fields) {
|
|
|
+ String column = field.getColumn();
|
|
|
+ Object a = ObjectUtils.ifNull(this.after, map -> map.get(column));
|
|
|
+ Object b = ObjectUtils.ifNull(this.before, map -> map.get(column));
|
|
|
+ if (!ObjectUtils.equals(a, b)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断数据是否更新
|
|
|
+ *
|
|
|
+ * @param columns 数据库字段数组
|
|
|
+ * @return true/false
|
|
|
+ */
|
|
|
+ public boolean isModified(@NonNull String... columns) {
|
|
|
+ if (ObjectUtils.notEmpty(this.after) && ObjectUtils.notEmpty(columns)) {
|
|
|
+ for (String column : columns) {
|
|
|
+ Object a = ObjectUtils.ifNull(this.after, map -> map.get(column));
|
|
|
+ Object b = ObjectUtils.ifNull(this.before, map -> map.get(column));
|
|
|
+ if (!ObjectUtils.equals(a, b)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 赋值实体对象更新后属性值
|
|
|
+ *
|
|
|
+ * @param entity 实体对象实例
|
|
|
+ * @param <T> 实体类型
|
|
|
+ */
|
|
|
+ public <T> void after(@NonNull T entity) {
|
|
|
+ update(entity, this.after);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 赋值实体对象更新后属性值
|
|
|
+ *
|
|
|
+ * @param model 实体模型
|
|
|
+ * @param <T> 实体类型
|
|
|
+ * @return 实体对象实例
|
|
|
+ */
|
|
|
+ public <T> T after(@NonNull Class<T> model) {
|
|
|
+ return convert(model, this.after);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 赋值实体对象更新前属性值
|
|
|
+ *
|
|
|
+ * @param entity 实体对象实例
|
|
|
+ * @param <T> 实体类型
|
|
|
+ */
|
|
|
+ public <T> void before(@NonNull T entity) {
|
|
|
+ update(entity, this.before);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 赋值实体对象更新前属性值
|
|
|
+ *
|
|
|
+ * @param model 实体模型
|
|
|
+ * @param <T> 实体类型
|
|
|
+ * @return 实体对象实例
|
|
|
+ */
|
|
|
+ public <T> T before(@NonNull Class<T> model) {
|
|
|
+ return convert(model, this.before);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取差异属性值
|
|
|
+ *
|
|
|
+ * @param model 实体模型
|
|
|
+ * @return 实体对象属性名/值映射表
|
|
|
+ */
|
|
|
+ public Map<String, Object> different(@NonNull Class<?> model) {
|
|
|
+ if (ObjectUtils.isEmpty(this.after)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> different = Maps.newHashMap();
|
|
|
+ TableInfo table = DatabaseContextHolder.getTable(model);
|
|
|
+ List<TableFieldInfo> fields = table.getFieldList();
|
|
|
+ if (ObjectUtils.isEmpty(fields)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+ for (TableFieldInfo field : fields) {
|
|
|
+ String column = field.getColumn();
|
|
|
+ Object a = ObjectUtils.ifNull(this.after, map -> map.get(column));
|
|
|
+ Object b = ObjectUtils.ifNull(this.before, map -> map.get(column));
|
|
|
+ if (!ObjectUtils.equals(a, b)) {
|
|
|
+ different.put(field.getProperty(), convert(field.getField(), a));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return different;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取差异字段值
|
|
|
+ *
|
|
|
+ * @param columns 数据库字段数组
|
|
|
+ * @return 数据库字段名/值映射表
|
|
|
+ */
|
|
|
+ public Map<String, Object> different(@NonNull String... columns) {
|
|
|
+ if (ObjectUtils.isEmpty(this.after)) {
|
|
|
+ return Collections.emptyMap();
|
|
|
+ }
|
|
|
+
|
|
|
+ Map<String, Object> different = Maps.newHashMap();
|
|
|
+ if (ObjectUtils.isEmpty(columns)) {
|
|
|
+ this.after.forEach((key, a) -> {
|
|
|
+ Object b = ObjectUtils.ifNull(this.before, map -> map.get(key));
|
|
|
+ if (!ObjectUtils.equals(a, b)) {
|
|
|
+ different.put(key, a);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ for (String column : columns) {
|
|
|
+ Object a = ObjectUtils.ifNull(this.after, map -> map.get(column));
|
|
|
+ Object b = ObjectUtils.ifNull(this.before, map -> map.get(column));
|
|
|
+ if (!ObjectUtils.equals(a, b)) {
|
|
|
+ different.put(column, a);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return different;
|
|
|
+ }
|
|
|
+}
|