1 Star 0 Fork 1

阿超 / simple-stream-query

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
MpUtil.java 18.82 KB
一键复制 编辑 原始数据 按行查看 历史
阿超 提交于 2023-03-21 20:20 . :trollface:bug fix
package com.ruben.simplestreamquery.util;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.ruben.simplestreamquery.pojo.bo.AttachBO;
import com.ruben.simplestreamquery.pojo.bo.BaseDbBO;
import com.ruben.simplestreamquery.pojo.bo.RelationBO;
import com.ruben.simplestreamquery.pojo.bo.SubBO;
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 org.apache.ibatis.type.SimpleTypeRegistry;
import org.springframework.beans.BeanUtils;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.time.temporal.TemporalAccessor;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Function;
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
*/
@SuppressWarnings("unchecked")
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,
K extends Comparable<? super K> & Serializable,
A,
L extends Comparable<? super L> & Serializable>
BaseDbBO<A> saveAttach(AttachBO<T, K, A> bo) {
val mainList = bo.getMainList();
val mainKeys = Steam.of(mainList).map(bo.getMainKey()).toList();
val attachKeyExecutable = LambdaHelper.resolve(bo.getAttachKey());
val attachKeySetter = getSetter(attachKeyExecutable);
val compareGetterSetterMap = Steam.of(bo.getAttachCompares())
.toMap(Function.identity(), MpUtil::getSetter);
val attachListFromClient = Steam.of(mainList).flat(m -> Steam.of(bo.getAttachGetter().apply(m))
.peek(a -> attachKeySetter.accept(a, bo.getMainKey().apply(m))))
.toList();
val attachGetterExecutable = LambdaHelper.resolve(bo.getAttachGetter());
val field = ReflectHelper.getField(attachGetterExecutable.getClazz(), BeanHelper.getPropertyName(attachGetterExecutable.getName()));
Class<A> genericType = (Class<A>) ReflectHelper.getGenericTypes(field.getGenericType())[0];
val attachListFromDb = Many.of(bo.getAttachKey()).in(mainKeys)
.value(v -> {
val vo = ((SerSupp<A>) genericType::newInstance).get();
BeanUtils.copyProperties(v, vo);
return vo;
})
.query();
val attachTableInfo = TableInfoHelper.getTableInfo(attachKeyExecutable.getClazz());
val attachPrimaryKey = MpUtil.<A, L>getGetter((Class<A>) attachKeyExecutable.getClazz(),
attachTableInfo.getKeyProperty());
val attachKeyListFromDb = Steam.of(attachListFromDb).map(attachPrimaryKey).toList();
if (attachKeyListFromDb.isEmpty()) {
bo.setWillInsertList(attachListFromClient);
bo.setDataList(attachListFromClient);
return bo;
}
if (attachListFromClient.isEmpty()) {
bo.setWillDeleteList(attachListFromDb);
return bo;
}
val mainIdAttachesMapFromDb = Steam.of(attachListFromDb).group(bo.getAttachKey());
val mainIdAttachesMapFromClient = Steam.of(attachListFromClient).group(bo.getAttachKey());
mainKeys.forEach(mainKey -> {
val attachesFromDb = mainIdAttachesMapFromDb.getOrDefault(mainKey, emptyList());
val attachesFromClient = mainIdAttachesMapFromClient.getOrDefault(mainKey, emptyList());
if (attachesFromDb.isEmpty() && attachesFromClient.isEmpty()) {
return;
}
if (attachesFromDb.isEmpty()) {
bo.getWillInsertList().addAll(attachesFromClient);
return;
}
if (attachesFromClient.isEmpty()) {
bo.getWillDeleteList().addAll(attachesFromDb);
return;
}
val idAttachMapFromDb = Steam.of(attachesFromDb).toMap(attachPrimaryKey);
// insert
Steam.of(attachesFromClient)
.filter(attach -> {
val id = attachPrimaryKey.apply(attach);
return Objects.isNull(id) || !idAttachMapFromDb.containsKey(id);
})
.forEach(bo.getWillInsertList()::add);
val idAttachMapFromClient = Steam.of(attachesFromClient)
.filter(attach -> {
val id = attachPrimaryKey.apply(attach);
return Objects.nonNull(id);
}).toMap(attachPrimaryKey);
// remove
val attachKeysFromDb = idAttachMapFromDb.keySet();
Steam.of(attachKeysFromDb)
.filter(id -> !idAttachMapFromClient.containsKey(id))
.map(idAttachMapFromDb::get)
.forEach(bo.getWillDeleteList()::add);
// modify
attachKeysFromDb.retainAll(idAttachMapFromClient.keySet());
attachKeysFromDb.forEach(attachKey -> {
val attachFromDb = idAttachMapFromDb.get(attachKey);
val attachFromClient = idAttachMapFromClient.get(attachKey);
if (Objects.nonNull(attachFromDb) && Objects.nonNull(attachFromClient)) {
Steam.of(bo.getAttachCompares()).forEach(ac -> {
val value = ac.apply(attachFromClient);
if (!Objects.equals(value, ac.apply(attachFromDb))) {
val executable = LambdaHelper.resolve((Serializable) ac);
val setter = compareGetterSetterMap.get(ac);
val fieldType = ReflectHelper.getField(executable.getClazz(),
BeanHelper.getPropertyName(executable.getName())).getType();
setter.accept(attachFromDb, value);
if (isComparable(fieldType) && (!bo.getWillUpdateList().contains(attachFromDb))) {
bo.getWillUpdateList().add(attachFromDb);
}
}
});
}
});
});
bo.setAfterExecuted(b -> {
attachListFromDb.removeIf(bo.getWillDeleteList()::contains);
attachListFromDb.addAll(bo.getWillInsertList());
val idAttachMap = Steam.of(attachListFromDb).toMap(attachPrimaryKey);
attachListFromClient.forEach(data -> {
val primaryKey = attachPrimaryKey.apply(data);
idAttachMap.put(primaryKey, data);
});
bo.setDataList(new ArrayList<>(idAttachMap.values()));
});
return bo;
}
public static <T, K extends Comparable<? super K> & Serializable, S> BaseDbBO<S> saveSub(SubBO<T, K, S> bo) {
val subIds = Steam.of(bo.getMainList())
.flat(data -> Steam.of(bo.getSubIdGetters()).map(f -> f.apply(data)))
.nonNull().toList();
val subClazz = (Class<S>) Steam.of(bo.getSubGetters())
.findFirst()
.map(LambdaHelper::resolve)
.map(LambdaExecutable::getReturnType)
.orElseThrow(() -> new IllegalStateException("sub class not found"));
val primaryKeyGetter = MpUtil.<S, K>getGetter(subClazz,
TableInfoHelper.getTableInfo(subClazz).getKeyProperty());
val idSubMapFromDb = subIds.isEmpty() ? new HashMap<K, S>() :
Steam.of(Database.listByIds(subIds, subClazz)).toMap(primaryKeyGetter);
val subIdGetterSetterMap = Steam.of(bo.getSubIdGetters())
.toMap(Function.identity(), MpUtil::getSetter);
val comparesGetterSetterMap = Steam.of(bo.getSubCompares())
.toMap(Function.identity(), MpUtil::getSetter);
Steam.of(bo.getMainList()).forEach(data -> {
Steam.of(bo.getSubIdGetters()).zip(bo.getSubGetters(),
(subIdGetter, subGetter) -> {
val subFromClient = subGetter.apply(data);
val subId = subIdGetter.apply(data);
if (Objects.isNull(subFromClient)) {
return null;
}
// insert
if (Objects.isNull(subId)) {
bo.getWillInsertList().add(subFromClient);
return subFromClient;
}
val subFromDb = idSubMapFromDb.get(subId);
if (Objects.isNull(subFromDb)) {
bo.getWillInsertList().add(subFromClient);
return subFromClient;
}
// update
Steam.of(bo.getSubCompares()).forEach(ac -> {
val value = ac.apply(subFromClient);
if (!Objects.equals(value, ac.apply(subFromDb))) {
val executable = LambdaHelper.resolve((Serializable) ac);
val setter = (BiConsumer<S, Object>) comparesGetterSetterMap.get(ac);
val fieldType = ReflectHelper.getField(executable.getClazz(),
BeanHelper.getPropertyName(executable.getName())).getType();
setter.accept(subFromDb, value);
if (isComparable(fieldType) && (!bo.getWillUpdateList().contains(subFromDb))) {
bo.getWillUpdateList().add(subFromDb);
}
}
});
return subFromClient;
}).toList();
});
bo.setAfterExecuted(b -> {
val dataList = Steam.of(bo.getMainList()).flat(data ->
Steam.of(bo.getSubIdGetters()).zip(bo.getSubGetters(),
(subIdGetter, subGetter) -> {
S subFromClient = subGetter.apply(data);
if (Objects.nonNull(subFromClient)) {
val primaryKey = primaryKeyGetter.apply(subFromClient);
subIdGetterSetterMap.get(subIdGetter).accept(data, primaryKey);
}
return subFromClient;
}).nonNull().toList()).toList();
bo.setDataList(dataList);
});
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);
}
public static boolean isComparable(Class<?> returnType) {
if (SimpleTypeRegistry.isSimpleType(returnType)) {
return true;
}
if (returnType.isEnum()) {
return true;
}
if (TemporalAccessor.class.isAssignableFrom(returnType)) {
return true;
}
if (Date.class.isAssignableFrom(returnType)) {
return true;
}
if (Collection.class.isAssignableFrom(returnType)) {
return false;
}
if (Map.class.isAssignableFrom(returnType)) {
return false;
}
if (BeanUtil.isBean(returnType)) {
return false;
}
return false;
}
}
Java
1
https://gitee.com/VampireAchao/simple-stream-query.git
git@gitee.com:VampireAchao/simple-stream-query.git
VampireAchao
simple-stream-query
simple-stream-query
master

搜索帮助