Java级联更新

在工作中经常会有关联性问题:例如 在用户表修改了用户名称 所有冗余了用户名称的表都需要进行修改 故产出了这篇文章 希望对大家有所帮助

1.准备工作

        我们需要新增注解 DomainSource ,DomainTarget,DomainProperty,DomainTargets
  @ DomainSource注解实体



import java.lang.annotation.*;

/**
 * @author : LvLiang
 * @effect : 源实体注解,定义在源实体类上
 * @date : 2023/6/26  13:57
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DomainSource {

    /**
     * 属性数组
     */
    String[] keys();
}

@DomainTarget注解实体



import java.lang.annotation.*;

/**
 * @author : LvLiang
 * @effect :目标实体注解,定义在目标有冗余的实体类上
 * @date : 2023/6/26  14:16
 *
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DomainTarget {

    /**
     * 包名
     *
     * @return
     */
    String packages();

    /**
     * 源实体名称
     *
     */
    String entityName();

    /**
     * 外键 表字段
     *
     * @return
     */
    String foreignKey();

    /**
     * 属性数组
     *
     */
    DomainProperty[] keys();


}

@DomainProperty 注解实体

import java.lang.annotation.*;

/**
 * @author : LvLiang
 * @effect :修改字段映射关系
 * @date : 2023/6/26  14:17
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DomainProperty {
    /**
     * 源实体中的属性名
     *
     */
    String source();

    /**
     * 目标实体中的属性名
     *
     */
    String target();
}

@DomainTargets注解实体



import java.lang.annotation.*;

/**
 * @author : LvLiang
 * @effect :
 * @date : 2023/8/1  16:51
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface DomainTargets {
    DomainTarget[] value();
}

消息封装


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import lombok.experimental.SuperBuilder;

import java.io.Serializable;
import java.util.List;

/**
 *
 * @author : LvLiang
 * @effect :领域消息事件对象,用于领域数据变动后数据的封装
 * @package: com.fastsun.platform.core.bean
 * @date : 2023/6/26  14:22
 */
@Data
@SuperBuilder
@NoArgsConstructor
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
public class DomainMessageEvent extends BaseEvent  {
    private static final long serialVersionUID = 5354621263610168362L;
//    /**
//     * 应用ID
//     *
//     */
//    private String appId;
//
//    /**
//     * 租户代码
//     *
//     */
//    private String tenantCode;

    /**
     * 包名
     */
    private String packages;

    /**
     * 源服务实体名
     *
     */
    private String entityName;

    /**
     * 修改的数据的主键ID
     */
    private Serializable key;


    /**
     * 源实体中修改的属性名及值
     *
     */
    private List<Modified> modifiedList;

}

修改属性及值类


import lombok.Builder;
import lombok.Data;
import lombok.ToString;

import java.io.Serializable;

/**
 *
 * @author : LvLiang
 * @effect : 修改的属性
 * @package: com.fastsun.platform.core.bean
 * @date : 2023/6/26  14:23
 */
@Data
@ToString
@Builder
public class Modified implements Serializable{
    private static final long serialVersionUID = 5353367948265675298L;
    /**
     * 修改属性名
     *
     */
    private String property;
    /**
     * 修改属性名的值
     *
     */
    private Object value;
}


消息发送类


import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fastsun.platform.core.annotation.DomainSource;
import com.fastsun.platform.core.bean.DomainMessageEvent;
import com.fastsun.platform.core.bean.Modified;
import com.fastsun.platform.core.configurer.ApplicationContextProvider;
import com.fastsun.platform.core.dto.TenantDTO;
import com.fastsun.platform.core.enums.FastsunEventType;
import com.fastsun.platform.core.event.IMessagePublisher;
import com.fastsun.platform.core.integrate.IEntityCommitListener;
import com.fastsun.platform.core.integrate.LocalApplicationContext;
import com.fastsun.platform.core.integrate.LocalTenantContext;
import com.fastsun.platform.core.utils.JsonUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.io.Serializable;
import java.util.*;

import static com.fastsun.platform.core.common.FastsunSettings.PLATFORM_MASTER_TENANT;

/**
 * @author : LvLiang
 * @effect : 数据修改发送类
 * @package: com.fastsun.platform.core.listeners
 * @date : 2023/6/26  15:51
 */
@Component
@RequiredArgsConstructor
public class DomainDataModificationSending implements IEntityCommitListener {

    @Autowired
    private IMessagePublisher messagePublisher;

    @Override
    public void update(String appId, TenantDTO tenant, Object entity, Serializable id, String[] names, Object[] oldState, Object[] state) {
        Class<?> clazz = entity.getClass();
        DomainSource annotation = clazz.getAnnotation(DomainSource.class);
        if (ObjectUtils.isEmpty(annotation)) {
            return;
        }
        String[] keys = annotation.keys();
        if (keys.length > 0){
            List<Modified> modifiedList = new ArrayList<>();
            for (String key : keys) {
                for (int i = 0 ; i < names.length; i++) {
                    String name = names[i];
                    if (Objects.equals(name, key)) {
                        if (!Objects.equals(oldState[i], state[i])) {
                            Modified value = Modified.builder().property(key).value(state[i]).build();
                            modifiedList.add(value);
                        }
                        break;
                    }
                }
            }
            if (modifiedList.size()>0){
                String name = ClassUtil.getClassName(clazz,true);
                DomainMessageEvent build = DomainMessageEvent.builder()
                        .type("update")
                        .key(id)
                        .appId(LocalApplicationContext.get())
                        .tenant(ObjectUtils.isEmpty(tenant) ? null : tenant.getTenantCode())
                        .entityName(name)
                        .packages(clazz.getPackage().getName())
                        .modifiedList(modifiedList)
                        .build();
                messagePublisher.publisher(FastsunEventType.REDUNDANCE, build);
            }
        }

    }

    @Override
    public void delete(String appId, TenantDTO tenant, Object entity, Serializable id) {
        Class<?> clazz = entity.getClass();
        DomainSource annotation = clazz.getAnnotation(DomainSource.class);
        if (ObjectUtils.isEmpty(annotation)) {
            return;
        }
        String code = tenant == null
                ? ApplicationContextProvider.getProperty("fastsun.platform.master.tenant")
                : tenant.getTenantCode();
        String name = ClassUtil.getClassName(clazz,true);
        DomainMessageEvent build = DomainMessageEvent.builder()
                .type("delete")
                .key(id)
                .tenant(code)
                .entityName(name)
                .modifiedList(new ArrayList<>())
                .build();
        messagePublisher.publisher(FastsunEventType.REDUNDANCE, build);
    }
}

消息处理类


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fastsun.platform.core.annotation.DomainProperty;
import com.fastsun.platform.core.annotation.DomainTarget;
import com.fastsun.platform.core.annotation.DomainTargets;
import com.fastsun.platform.core.bean.DomainMessageEvent;
import com.fastsun.platform.core.bean.Modified;
import com.fastsun.platform.core.configurer.ApplicationContextProvider;
import com.fastsun.platform.core.dto.TenantDTO;
import com.fastsun.platform.core.event.IMessageListener;
import com.fastsun.platform.core.integrate.ICacheService;
import com.fastsun.platform.core.integrate.LocalApplicationContext;
import com.fastsun.platform.core.integrate.LocalTenantContext;
import com.fastsun.platform.core.utils.ClassUtils;
import lombok.extern.slf4j.Slf4j;
import org.reflections.Reflections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import javax.persistence.Table;
import java.io.Serializable;
import java.util.*;
import java.util.stream.Collectors;

import static com.fastsun.platform.core.common.FastsunSettings.PLATFORM_BASE_PACKAGES;
import static com.fastsun.platform.core.common.SystemConstant.TENANT_CACHE_KEY_NAME;

/**
 * @author : LvLiang
 * @effect : 数据修改消费类
 * @package: com.fastsun.platform.core.listeners
 * @date : 2023/6/28  13:42
 */
@Slf4j
@Component("redundance")
public class DomainDataSyncMessageListener implements IMessageListener {

    @Autowired
    private ICacheService cache;

    @Autowired
    private JdbcTemplate jdbc;


    @Override
    public void execute(String topic, String json) {
        log.error("消费topic【" + topic + "】!,更新json【"+json+"】");
        DomainMessageEvent event = establishEvent(json);
        if (!ObjectUtils.isEmpty(event.getAppId())) {
            LocalApplicationContext.set(event.getAppId());
        }
        if (!ObjectUtils.isEmpty(event.getTenant())) {
            TenantDTO tenantDTO = (TenantDTO) cache.getMapValue(TENANT_CACHE_KEY_NAME, event.getTenant());
            LocalTenantContext.set(tenantDTO);
        }
        String packages = ApplicationContextProvider.getProperty(PLATFORM_BASE_PACKAGES, "com.fastsun");
        Reflections reflections = new Reflections(packages);
        Set<Class<?>> domainTargetSet = reflections.getTypesAnnotatedWith(DomainTarget.class);
        domainTargetSet.addAll(reflections.getTypesAnnotatedWith(DomainTargets.class));
        if (Objects.equals(event.getType(), "update")) {
            doUpdate(event, domainTargetSet);
        } else if (Objects.equals(event.getType(), "delete")) {
            doDelete(event, domainTargetSet);
        } else {
            log.error("未知的操作类型【" + event.getType() + "】!");
        }

    }

    private DomainMessageEvent establishEvent(String json) {

        DomainMessageEvent domainMessageEvent = new DomainMessageEvent();
        JSONObject obj = JSONUtil.parseObj(json);
        if (ObjectUtil.equals(obj.get("type"), "update")) {

            domainMessageEvent.setKey(obj.get("key", Serializable.class));
            domainMessageEvent.setEntityName(obj.get("entityName", String.class));
            domainMessageEvent.setTenant(obj.get("tenantCode", String.class));
            domainMessageEvent.setType(obj.get("type", String.class));
            ArrayList modifiedList = obj.get("modifiedList", ArrayList.class);
            List<Modified> modifies = new ArrayList<>();
            modifiedList.forEach(o -> {
                JSONObject jsonObject1 = JSONUtil.parseObj(o);
                Modified build = Modified.builder().property(jsonObject1.get("property", String.class)).value(jsonObject1.get("value", Object.class)).build();
                modifies.add(build);
            });
            domainMessageEvent.setModifiedList(modifies);
        }
        return domainMessageEvent;
    }

    private void doUpdate(DomainMessageEvent event, Set<Class<?>> domainTargetSet) {
        log.error("更新domainTargetSet【"+domainTargetSet+"】");
        log.error("更新event【"+event+"】");
        try {
            domainTargetSet.forEach(o -> {
                DomainTarget domainTarget = o.getAnnotation(DomainTarget.class);
                DomainTargets domainTargets = o.getAnnotation(DomainTargets.class);
                if (ObjectUtil.isNotEmpty(domainTarget)){
                    cascadeModification(domainTarget,o,event);
                }
                if (ObjectUtil.isNotEmpty(domainTargets)){
                    DomainTarget[] value = domainTargets.value();
                    for (DomainTarget target : value) {
                        cascadeModification(target,o,event);
                    }
                }
            });
        } catch (Exception e) {
            log.error("同步冗余数据失败!", e);
        }
    }

    private void cascadeModification(DomainTarget domainTarget, Class<?> o, DomainMessageEvent event) {
       if (ObjectUtil.equal(domainTarget.entityName(),event.getEntityName()) && ObjectUtil.equal(domainTarget.packages(),o.getPackage().getName())){
           Table table = o.getAnnotation(Table.class);
           List<DomainProperty> keys = Arrays.asList(domainTarget.keys());
           List<DomainProperty> domainProperties = keys.stream()
                   .filter(p -> event.getModifiedList().stream()
                           .map(Modified::getProperty)
                           .collect(Collectors.toList())
                           .contains(p.source()))
                   .collect(Collectors.toList());
           StringBuilder sql = new StringBuilder();
           sql.append("update ");
           sql.append(table.name());
           sql.append(" set ");
           for (int i = domainProperties.size() - 1; i >= 0; i--) {
               DomainProperty d = domainProperties.get(i);
               Object value = event.getModifiedList().stream()
                       .filter(modified -> Objects.equals(modified.getProperty(), d.source()))
                       .map(Modified::getValue)
                       .collect(Collectors.toList()).stream().findFirst().orElse(null);
               String columnForField = ClassUtils.getColumnForField(o, d.target());
               sql.append(columnForField);
               sql.append("=");
               sql.append(Objects.isNull(value) ? "" : value instanceof Boolean ? (Boolean) value : "'" + value + "'");
               if (i > 0) {
                   sql.append(",");
               }
           }
           sql.append(" where ");
           sql.append(domainTarget.foreignKey());
           sql.append("=");
           sql.append("'" + event.getKey() + "'");
           jdbc.update(sql.toString());
       }else {
           log.error("sql未执行,具体原因为:实体名比对:{},包名比对:{}" ,ObjectUtil.equal(domainTarget.entityName(),event.getEntityName()),ObjectUtil.equal(domainTarget.packages(),o.getPackage().getName()));
       }
    }



}

使用源数据注解


import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fastsun.platform.core.annotation.Comment;
import com.fastsun.platform.core.annotation.DBTable;
import com.fastsun.platform.core.annotation.DomainSource;
import com.fastsun.platform.core.bean.BaseEntity;
import lombok.Data;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;


@Entity
@DBTable(name = "商品分类")
@Table(name = "classification")
@Data
@DomainSource(keys = {"fullPath","classificationName"})
public class Classification extends BaseEntity {


    @Column(name = "classification_code",length = 80)
    @Comment(name = "分类代码")
    private String classificationCode;


    @Column(name = "classification_name",length = 150)
    @Comment(name = "分类名称")
    private String classificationName;

    @Column(name = "parent_category_id")
    @Comment(name = "父分类ID")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long parentCategoryID;

    @Column(name = "parent_category_name",length = 150)
    @Comment(name = "父分类名称")
    private String parentCategoryName;

    @Column(name = "full_path",length = 300)
    @Comment(name = "全路径")
    private String fullPath;

    @Column(name = "full_path_id",length = 300)
    @Comment(name = "全路径Id")
    private String fullPathId;

}

修改类
 



import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import lombok.Data;
import org.hibernate.annotations.Where;

import javax.persistence.*;
import java.math.BigDecimal;
import java.util.Date;

@Data
@Entity
@DBTable(name = "商品")
@Table(name = "Product")
@DomainTarget(entityName = "Classification",foreignKey = "CLASSIFICATION_ID",
        keys = {
        @DomainProperty(target = "classificationName",source = "classificationName")
        ,@DomainProperty(target = "fullPath",source = "fullPath")
        ,@DomainProperty(target = "fullPathId",source = "fullPathId")
        },
        packages = "com.fastsun.platform.centralized.purchasing.domain.entity"
)
public class Product extends BaseAuditEntity {

    @Column(name = "PRODUCT_CODE", nullable = false, length = 20)
    @Comment(name = "商品编码")
    private String code;

    @Comment(name = "商品名称", type = DataType.VARCHAR, length = 200)
    @Column(name = "PRODUCT_NAME", length = 200, nullable = false)
    private String name;
 @Comment(name = "分类ID", type = DataType.LONG)
    @Column(name = "CLASSIFICATION_ID")
    private Long categoryId;

    @Column(name = "classification_name",length = 150)
    @Comment(name = "分类名称")
    private String classificationName;


    @Column(name = "full_path",length = 300)
    @Comment(name = "分类全路径")
    private String fullPath;

    @Column(name = "full_path_id",length = 300)
    @Comment(name = "分类全路径ID")
    private String fullPathId;

}

增加该注解后  编辑分类后会触发修改商品分类信息

希望本文档可以给各位提到帮助
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值