com.power4j.fist.boot.mybaits.tree.AbstractNodeIdxSupport Maven / Gradle / Ivy
/*
* Copyright 2021 ChenJun ([email protected] & https://github.com/John-Chan)
*
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.gnu.org/licenses/lgpl.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.power4j.fist.boot.mybaits.tree;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.power4j.fist.boot.mybaits.crud.repository.Repository;
import com.power4j.fist.data.tree.domain.NodeIdx;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.lang.Nullable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* 负责维护数据库中树形结构的节点关系
*
* @author CJ ([email protected])
* @date 2021/9/23
* @since 1.0
* @param NodeIdx 的子类
* @param ID 类型
* @param Repository
*/
public abstract class AbstractNodeIdxSupport, ID extends Serializable, R extends Repository>
implements TreePathSupport {
/**
* Link Repository
* @return Repository
*/
protected abstract R getRepository();
/**
* 创建对象
* @param ancestor 祖先节点ID
* @param descendant 后代节点ID
* @param distance 层距离
* @return C
*/
protected abstract T createObject(ID ancestor, ID descendant, int distance);
/**
* 创建一个指向自己的 Path
* @param id 节点ID
* @return C
*/
protected T createObject(ID id) {
return createObject(id, id, 0);
}
@Override
public long countAllDescendant(ID id, @Nullable Integer distanceMin, @Nullable Integer distanceMax) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.eq(T::getAncestor,id)
.ge(Objects.nonNull(distanceMin), T::getDistance, distanceMin)
.le(Objects.nonNull(distanceMax), T::getDistance, distanceMax);
return getRepository().countBy(wrapper);
// @formatter:on
}
@Override
public List findAllDescendant(ID id, @Nullable Integer distanceMin, @Nullable Integer distanceMax) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.eq(T::getAncestor,id)
.ge(Objects.nonNull(distanceMin), T::getDistance, distanceMin)
.le(Objects.nonNull(distanceMax), T::getDistance, distanceMax);
return getRepository().findAllBy(wrapper);
// @formatter:on
}
@Override
public List findAllDescendant(Collection ids, @Nullable Integer distanceMin, @Nullable Integer distanceMax) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.in(T::getAncestor,ids)
.ge(Objects.nonNull(distanceMin), T::getDistance, distanceMin)
.le(Objects.nonNull(distanceMax), T::getDistance, distanceMax);
return getRepository().findAllBy(wrapper);
// @formatter:on
}
@Override
public long countAllAncestor(ID id, @Nullable Integer distanceMin, @Nullable Integer distanceMax) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.eq(T::getAncestor,id)
.ge(Objects.nonNull(distanceMin), T::getDistance, distanceMin)
.le(Objects.nonNull(distanceMax), T::getDistance, distanceMax);
return getRepository().countBy(wrapper);
// @formatter:on
}
@Override
public List findAllAncestor(ID id, @Nullable Integer distanceMin, @Nullable Integer distanceMax) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.eq(T::getDescendant,id)
.ge(Objects.nonNull(distanceMin), T::getDistance, distanceMin)
.le(Objects.nonNull(distanceMax), T::getDistance, distanceMax);
return getRepository().findAllBy(wrapper);
// @formatter:on
}
@Override
public List findAllAncestor(Collection ids, @Nullable Integer distanceMin, @Nullable Integer distanceMax) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.in(T::getDescendant,ids)
.ge(Objects.nonNull(distanceMin), T::getDistance, distanceMin)
.le(Objects.nonNull(distanceMax), T::getDistance, distanceMax);
return getRepository().findAllBy(wrapper);
// @formatter:on
}
@Override
public List getAll(@Nullable Integer distanceMin, @Nullable Integer distanceMax) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.ge(Objects.nonNull(distanceMin), T::getDistance, distanceMin)
.le(Objects.nonNull(distanceMax), T::getDistance, distanceMax);
return getRepository().findAllBy(wrapper);
// @formatter:on
}
/**
* 删除节点关系,当节点删除后应该使用此方法
* @param id 节点ID,与该节点相关的所有路径会被删除
*/
@Override
public void removeAllPath(ID id) {
// @formatter:off
LambdaQueryWrapper wrapper = getRepository().lambdaWrapper()
.eq(T::getAncestor,id)
.or()
.eq(T::getDescendant,id);
getRepository().deleteAllBy(wrapper);
// @formatter:on
}
/**
* 添加路径信息,新增节点时应该使用此方法
* @param newNode 新增节点的ID
* @param parent 父节点,可以为空
*/
@Override
@Transactional(rollbackFor = Exception.class)
public void generatePath(ID newNode, @Nullable ID parent) {
Assert.notNull(newNode, "new node can not null");
// @formatter:off
if(Objects.nonNull(parent)){
List pathList = findAllAncestor(parent,null,null)
.stream().map(o -> createObject(o.getAncestor(),newNode,o.getDistance() + 1))
.collect(Collectors.toList());
getRepository().saveAll(pathList);
}
// @formatter:on
getRepository().saveOne(createObject(newNode));
}
@Override
public Set subTreeNodes(Collection ids) {
if (ObjectUtils.isEmpty(ids)) {
return Collections.emptySet();
}
List paths = findAllDescendant(ids, null, null);
// @formatter:off
return paths.stream()
.map(NodeIdx::getDescendant)
.collect(Collectors.toSet());
// @formatter:on
}
@Override
public Set findSharedRoot(Collection ids) {
if (ObjectUtils.isEmpty(ids)) {
return Collections.emptySet();
}
List paths = findAllAncestor(ids, null, null);
Map> map = paths.stream().collect(Collectors.groupingBy(NodeIdx::getDescendant));
Set roots = new HashSet<>(8);
// @formatter:off
map.values().forEach(list -> {
list.stream().max(Comparator.comparing(NodeIdx::getDistance))
.ifPresent(o -> roots.add(o.getAncestor()));
});
// @formatter:on
return roots;
}
protected LambdaQueryWrapper allEq(@Nullable Long ancestor, @Nullable Long descendant,
@Nullable Integer distance) {
// @formatter:off
return getRepository().lambdaWrapper()
.eq(Objects.nonNull(ancestor), T::getAncestor,ancestor)
.eq(Objects.nonNull(descendant), T::getDescendant, descendant)
.eq(Objects.nonNull(distance), T::getDistance, distance);
// @formatter:on
}
}