关联表更新封装

简介: 关联表更新封装

凭自己的本事和正当手段挣来的钱财,可以使我们赢得道义和幸福——阿基兰

分享一个关联更新函数

package com.ruben.simplestreamquery.util;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.ruben.simplestreamquery.pojo.bo.RelationBO;
import io.github.vampireachao.stream.core.bean.BeanHelper;
import io.github.vampireachao.stream.core.lambda.LambdaExecutable;
import io.github.vampireachao.stream.core.lambda.LambdaHelper;
import io.github.vampireachao.stream.core.lambda.function.SerSupp;
import io.github.vampireachao.stream.core.reflect.ReflectHelper;
import io.github.vampireachao.stream.core.stream.Steam;
import io.github.vampireachao.stream.plugin.mybatisplus.Database;
import io.github.vampireachao.stream.plugin.mybatisplus.Many;
import lombok.val;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import static cn.hutool.core.text.CharSequenceUtil.genSetter;
import static java.util.Collections.emptyList;
/**
 * MpUtil
 *
 * @author VampireAchao
 * @since 2023/3/15
 */
public class MpUtil {
    public static <T,
            K extends Comparable<? super K> & Serializable,
            A,
            U extends Comparable<? super U> & Serializable,
            R> BaseDbBO<R> saveRelation(RelationBO<T, K, A, U, R> bo) {
        val mainList = bo.getMainList();
        val mainKeys = Steam.of(mainList).map(bo.getMainKey()).toList();
        val relationMainGetter = LambdaHelper.resolve(bo.getRelationMain());
        val relationMainSetter = getSetter(bo.getRelationMain());
        val relationAttachSetter = getSetter(bo.getRelationAttach());
        val relationClass = (Class<?>) relationMainGetter.getInstantiatedTypes()[0];
        val constructor = ((SerSupp<Constructor<?>>) relationClass::getConstructor).get();
        val constructorLambda = LambdaHelper.revert(Supplier.class, constructor);
        val nameRelationCompareSetterMap = Steam.of(bo.getRelationCompares())
                .map(c -> LambdaHelper.resolve((Serializable) c))
                .<String, BiConsumer<R, Object>>toMap(LambdaExecutable::getName, MpUtil::getSetter);
        val willInsertList = Steam.of(mainList).flatMap(vo -> Steam.of(bo.getAttachGetter().apply(vo)))
                .filter(bo.getAttachKey(), null)
                .toList();
        if (!willInsertList.isEmpty()) {
            if (bo.getInsertOnMissAttach()) {
                Database.saveFewSql(willInsertList);
            } else {
                throw new IllegalStateException("attach data missing");
            }
        }
        val relationsFromClient = Steam.of(mainList)
                .flatMap(vo -> Steam.of(bo.getAttachGetter().apply(vo))
                        .map(attach -> {
                            R relation = (R) constructorLambda.get();
                            relationMainSetter.accept(relation, bo.getMainKey().apply(vo));
                            relationAttachSetter.accept(relation, bo.getAttachKey().apply(attach));
                            Steam.of(bo.getAttachCompares()).forEach(c -> {
                                val executable = LambdaHelper.resolve((Serializable) c);
                                val setter = nameRelationCompareSetterMap.get(executable.getName());
                                setter.accept(relation, c.apply(attach));
                            });
                            return relation;
                        })
                ).toList();
        val relationMain = bo.getRelationMain();
        val relationsFromDb = Many.of(relationMain).in(mainKeys).query();
        if (relationsFromClient.isEmpty()) {
            bo.setWillDeleteList(relationsFromDb);
            return bo;
        }
        if (relationsFromDb.isEmpty()) {
            bo.setWillInsertList(relationsFromClient);
            bo.setDataList(relationsFromClient);
            return bo;
        }
        val mainIdRelationsMapFromDb = Steam.of(relationsFromDb)
                .group(relationMain);
        val mainIdRelationsMapFromClient = Steam.of(relationsFromClient)
                .group(relationMain);
        mainKeys.forEach(mainKey -> {
            val relationListFromDb = mainIdRelationsMapFromDb.getOrDefault(mainKey, emptyList());
            val relationListFromClient = mainIdRelationsMapFromClient.getOrDefault(mainKey, emptyList());
            if (relationListFromDb.isEmpty() && relationListFromClient.isEmpty()) {
                return;
            }
            if (relationListFromDb.isEmpty()) {
                bo.getWillInsertList().addAll(relationListFromClient);
                return;
            }
            if (relationListFromClient.isEmpty()) {
                bo.getWillDeleteList().addAll(relationListFromDb);
                return;
            }
            val attachKeyRelationMapFromClient = Steam.of(relationListFromClient)
                    .toMap(bo.getRelationAttach());
            val attachKeysFromClient = attachKeyRelationMapFromClient.keySet();
            val attachKeyRelationMapFromDb = Steam.of(relationListFromDb)
                    .toMap(bo.getRelationAttach());
            val attachKeysFromDb = attachKeyRelationMapFromDb.keySet();
            // insert
            Steam.of(attachKeysFromClient).filter(attachKey -> !attachKeysFromDb.contains(attachKey))
                    .map(attachKeyRelationMapFromClient::get)
                    .forEach(bo.getWillInsertList()::add);
            // remove
            Steam.of(attachKeysFromDb).filter(attachKey -> !attachKeysFromClient.contains(attachKey))
                    .map(attachKeyRelationMapFromDb::get)
                    .forEach(bo.getWillDeleteList()::add);
            // modify
            attachKeysFromDb.retainAll(attachKeysFromClient);
            attachKeysFromDb.forEach(attachKey -> {
                val relationFromDb = attachKeyRelationMapFromDb.get(attachKey);
                val relationFromClient = attachKeyRelationMapFromClient.get(attachKey);
                if (Objects.nonNull(relationFromDb) && Objects.nonNull(relationFromClient)) {
                    Steam.of(bo.getRelationCompares()).forEach(ac -> {
                        val value = ac.apply(relationFromClient);
                        if (!Objects.equals(value, ac.apply(relationFromDb))) {
                            val executable = LambdaHelper.resolve((Serializable) ac);
                            val setter = nameRelationCompareSetterMap.get(executable.getName());
                            setter.accept(relationFromDb, value);
                            if (!bo.getWillUpdateList().contains(relationFromDb)) {
                                bo.getWillUpdateList().add(relationFromDb);
                            }
                        }
                    });
                }
            });
        });
        bo.setAfterExecuted(b -> {
            val relationPrimaryKey = MpUtil.getGetter((Class<R>) relationClass,
                    TableInfoHelper.getTableInfo(relationClass).getKeyProperty());
            relationsFromDb.removeIf(bo.getWillDeleteList()::contains);
            relationsFromDb.addAll(bo.getWillInsertList());
            val idAttachMap = Steam.of(relationsFromDb).toMap(relationPrimaryKey);
            relationsFromClient.forEach(data -> {
                val pk = relationPrimaryKey.apply(data);
                idAttachMap.put(pk, data);
            });
            bo.setDataList(new ArrayList<>(idAttachMap.values()));
        });
        return bo;
    }
    public static <T, R> SFunction<T, R> getGetter(Class<T> clazz, String property) {
        return LambdaHelper.revert(
                SFunction.class,
                ReflectHelper.getMethod(
                        clazz,
                        StrUtil.genGetter(property)
                )
        );
    }
    public static <T, R> BiConsumer<T, R> getSetter(SFunction<T, R> getter) {
        return getSetter(LambdaHelper.resolve(getter));
    }
    public static <T, R> BiConsumer<T, R> getSetter(LambdaExecutable executable) {
        val setterName = genSetter(BeanHelper.getPropertyName(executable.getName()));
        val setter = Steam.of(ReflectHelper.getMethods(executable.getClazz()))
                .findFirst(m -> m.getName().equals(setterName))
                .orElse(null);
        return LambdaHelper.revert(BiConsumer.class, setter);
    }
}


用到的bo

import io.github.vampireachao.stream.core.lambda.function.SerCons;
import io.github.vampireachao.stream.plugin.mybatisplus.Database;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
/**
 * @author VampireAchao
 * @since 2023/3/16 11:07
 */
@Data
public abstract class BaseDbBO<T> {
    private List<T> willInsertList;
    private List<T> willDeleteList;
    private List<T> willUpdateList;
    private List<T> dataList;
    private Consumer<BaseDbBO<T>> afterExecuted;
    public BaseDbBO() {
        dataList = new ArrayList<>();
        willInsertList = new ArrayList<>();
        willDeleteList = new ArrayList<>();
        willUpdateList = new ArrayList<>();
        afterExecuted = SerCons.nothing();
    }
    public List<T> execute() {
        Database.removeByIds(getWillDeleteList());
        Database.saveFewSql(getWillInsertList());
        Database.updateFewSql(getWillUpdateList());
        afterExecuted.accept(this);
        return dataList;
    }
    public BaseDbBO<T> add(BaseDbBO<T> bo) {
        this.getWillInsertList().addAll(bo.getWillInsertList());
        this.getWillDeleteList().addAll(bo.getWillDeleteList());
        this.getWillUpdateList().addAll(bo.getWillUpdateList());
        this.afterExecuted = SerCons.multi(this.afterExecuted::accept, bo.getAfterExecuted()::accept);
        return this;
    }
}

使用方式:

MpUtil.saveRelation(new RelationBO<RuleParamVO, Integer, PositionVO, String, PositionRule>() {{
    setMainList(rules);
    setMainKey(RuleParamVO::getId);
    setAttachKey(PositionVO::getPositionId);
    setAttachGetter(RuleParamVO::getPositions);
    setRelationMain(PositionRule::getRuleId);
    setRelationAttach(PositionRule::getPositionId);
}}).execute();
MpUtil.saveRelation(new RelationBO<RuleParamVO, Integer, SysDepartVO, String, DepartRuleParam>() {{
    setMainList(rules);
    setMainKey(RuleParamVO::getId);
    setAttachKey(SysDepartVO::getDepartId);
    setAttachGetter(RuleParamVO::getDepartments);
    setRelationMain(DepartRuleParam::getRuleParamId);
    setRelationAttach(DepartRuleParam::getDepartmentId);
}});
MpUtil.saveRelation(new RelationBO<RuleParamVO, Integer, PositionVO, String, MemberRule>() {{
    setMainList(rules);
    setMainKey(RuleParamVO::getId);
    setAttachKey(PositionVO::getPositionId);
    setAttachGetter(RuleParamVO::getMembers);
    setRelationMain(MemberRule::getRuleId);
    setRelationAttach(MemberRule::getPositionId);
}});
MpUtil.saveRelation(new RelationBO<RuleParamVO, Integer, StageVO, Integer, RuleStageParam>() {{
    setMainList(rules);
    setMainKey(RuleParamVO::getId);
    setAttachKey(StageVO::getStageId);
    setAttachGetter(RuleParamVO::getStages);
    setRelationMain(RuleStageParam::getRuleParamId);
    setRelationAttach(RuleStageParam::getStageId);
}});
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
 * @author VampireAchao
 * @since 2023/3/6 11:32
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class RelationBO<T,
        K extends Comparable<? super K> & Serializable,
        A,
        U extends Comparable<? super U> & Serializable,
        R> extends BaseDbBO<R> {
    private List<T> mainList;
    private SFunction<T, K> mainKey;
    private SFunction<A, U> attachKey;
    private SFunction<T, List<A>> attachGetter;
    private SFunction<R, K> relationMain;
    private SFunction<R, U> relationAttach;
    private List<SFunction<A, Object>> attachCompares;
    private List<SFunction<R, Object>> relationCompares;
    private Boolean insertOnMissAttach;
    public RelationBO() {
        super();
        attachCompares = new ArrayList<>();
        relationCompares = new ArrayList<>();
        insertOnMissAttach = false;
    }
}

相关解释如下:

相关文章
|
8月前
|
数据库 Python
使用自关联方法处理多表关系
使用自关联方法处理多表关系
|
9月前
逻辑删除
逻辑删除
37 0
关联更新封装(二)
关联更新封装(二)
67 2
关联更新封装(三)
关联更新封装(三)
66 2
|
9月前
|
关系型数据库 MySQL 数据库
MySQL数据库-关联更新
MySQL数据库-关联更新
66 0
|
9月前
activiti并行网关执行时每个关联表的变化
activiti并行网关执行时每个关联表的变化
146 0
|
关系型数据库 MySQL 数据库连接
SQLAlchemy关联表删除策略设置
SQLAlchemy关联表删除策略设置
|
存储 编译器 程序员
C++数据定义及相关操作
C++数据定义及相关操作
151 0
C++数据定义及相关操作
|
SQL 关系型数据库 MySQL
mysql实战:左表数据全部展示,关联表有关联数据返回1,没有关联数据返回0
现在有一消息通知功能,后台发布的每条通知消息都会展示到APP端消息列表中,每条消息支持是否已读操作,从消息列表中点击进入详情视为完成已读操作;现在需要在查询出的用户消息列表信息,其中所有的通知消息信息要标注出是否已读.
mysql实战:左表数据全部展示,关联表有关联数据返回1,没有关联数据返回0
流程定义查询和删除
流程定义查询流程定义查询和删除