多级目录SQL分层查询

需求:有多级目录,而目录的层级是不固定的,如下图所示:

数据结构:

sql语句:

sql 复制代码
<select id="getList" resultType="com.hikvision.idatafusion.dhidata.bean.vo.knowledgebase.KnowledgeBaseTypeTreeVo">
    with recursive tree as(
        select distinct kbt.id, kbt.type_name, kbt.type_sort, kbt.parent_id, 1 as type_level,
               kbt.id::text as id_path, kbt.type_name::text as type_name_path, kbt.type_sort::text as type_sort_path
        from knowledge_base_type kbt
        <if test="excludeNoDataType">
            join knowledge_base kb on kb.delete_flag = 0 and kb.type_id_list <![CDATA[&&]]> array[kbt.id]::int8[]
                    and kb.publish_state = '1'
        </if>
        where kbt.delete_flag = 0
        union
        select tree.id, tree.type_name, tree.type_sort, parent.parent_id, tree.type_level + 1 as type_level,
            concat(parent.id, '/', tree.id_path) as id_path,
            concat(parent.type_name, '/', tree.type_name_path) as type_name_path,
            concat(parent.type_sort, '/', tree.type_sort_path) as type_sort_path
        from tree
        join knowledge_base_type parent on parent.delete_flag = 0 and parent.id = tree.parent_id
    )
    select id, type_name, type_sort, type_level, id_path, type_name_path, type_sort_path
    from (
        select *,  ROW_NUMBER() OVER (PARTITION BY id ORDER BY type_level desc) AS rn from tree
    ) t
    where t.rn = 1
</select>

业务处理 :

java 复制代码
/**
 * 是否排除没有数据的分类
 */
@Override
public List<KnowledgeBaseTypeTreeVo> getList(boolean excludeNoDataType) {
    KnowledgeBaseTypeQueryDto knowledgeBaseTypeQueryDto = new KnowledgeBaseTypeQueryDto();
    knowledgeBaseTypeQueryDto.setExcludeNoDataType(excludeNoDataType);
    List<KnowledgeBaseTypeTreeVo> knowledgeBaseTypeTreeVoList  = knowledgeBaseTypeDao.getList(knowledgeBaseTypeQueryDto);
    // 对分类进行分层、排序
    Map<Long, KnowledgeBaseTypeTreeVo> firstLevelMap = new HashMap<>(); // 一级分类
    for (KnowledgeBaseTypeTreeVo knowledgeBaseType : knowledgeBaseTypeTreeVoList) {
        String[] idPath = knowledgeBaseType.getIdPath().split("/");
        // 生成一级分类
        long firstLevelId = Long.parseLong(idPath[0]);
        if(!firstLevelMap.containsKey(firstLevelId)) {
            KnowledgeBaseTypeTreeVo firstLevel = new KnowledgeBaseTypeTreeVo();
            firstLevel.setId(firstLevelId);
            firstLevel.setTypeName(knowledgeBaseType.getTypeNamePath().split("/")[0]);
            firstLevel.setTypeSort(Integer.parseInt(knowledgeBaseType.getTypeSortPath().split("/")[0]));
            firstLevel.setParentId(knowledgeBaseType.getParentId());
            firstLevelMap.put(firstLevelId, firstLevel);
        }
        // 遍历生成二级、三级...分类
        KnowledgeBaseTypeTreeVo firstLevel = firstLevelMap.get(firstLevelId); // 一级
        Map<Long, KnowledgeBaseTypeTreeVo> childrenMap = firstLevel.getChildrenMap();
        for (int i = 1; i < idPath.length; i++) { // 从二级开始遍历
            long typeId = Integer.parseInt(idPath[i]);
            if(!childrenMap.containsKey(typeId)) {
                KnowledgeBaseTypeTreeVo treeVo = new KnowledgeBaseTypeTreeVo();
                treeVo.setId(typeId);
                treeVo.setTypeName(knowledgeBaseType.getTypeNamePath().split("/")[i]);
                treeVo.setTypeSort(Integer.parseInt(knowledgeBaseType.getTypeSortPath().split("/")[i]));
                treeVo.setParentId((long) Integer.parseInt(idPath[i-1]));
                childrenMap.put(typeId, treeVo);
            }
            childrenMap = childrenMap.get(typeId).getChildrenMap(); // 下一级
        }
        // 排序
        for(KnowledgeBaseTypeTreeVo firstKnowledgeBaseType : firstLevelMap.values()) {
            this.sortChildren(firstKnowledgeBaseType);
        }
        knowledgeBaseType.setChildrenMap(firstLevelMap);
    }
    // 结果处理后返回
    List<KnowledgeBaseTypeTreeVo> knowledgeBaseTypeTreeList = firstLevelMap.values().stream().collect(Collectors.toList());
    return knowledgeBaseTypeTreeList;
}

/*
* 分类按TypeSort倒序
* */
private void sortChildren(KnowledgeBaseTypeTreeVo treeVo) {
    if(MapUtils.isEmpty(treeVo.getChildrenMap())) {
        return;
    }
    treeVo.setChildren(treeVo.getChildrenMap().values().stream().
            sorted(Comparator.comparingInt(KnowledgeBaseTypeTreeVo::getTypeSort).reversed()).collect(Collectors.toList()));
    for(KnowledgeBaseTypeTreeVo child: treeVo.getChildrenMap().values()) {
        sortChildren(child);
    }
}

返回数据实体类:

java 复制代码
@Data
@ToString
@EqualsAndHashCode(callSuper = false)
public class KnowledgeBaseTypeTreeVo implements Serializable {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "ID")
    private Long id;

    @ApiModelProperty(value = "类型名称")
    private String typeName;

    @ApiModelProperty(value = "序号,排序")
    private Integer typeSort;

    @ApiModelProperty(value = "父节点ID")
    public Long parentId;

    @ApiModelProperty(value = "分类层级")
    private Integer typeLevel;

    @ApiModelProperty(value = "分类ID层级路径")
    private String idPath;

    @ApiModelProperty(value = "分类名称层级路径")
    private String typeNamePath;

    @ApiModelProperty(value = "分类排序层级路径")
    private String typeSortPath;

    @ApiModelProperty(value = "子分类")
    private List<KnowledgeBaseTypeTreeVo> children = new ArrayList();

    @ApiModelProperty(value = "分类")
    private Map<Long, KnowledgeBaseTypeTreeVo> childrenMap = new HashMap<>();
}
相关推荐
小江的记录本2 分钟前
【事务】Spring Framework核心——事务管理:ACID特性、隔离级别、传播行为、@Transactional底层原理、失效场景
java·数据库·分布式·后端·sql·spring·面试
sheji34169 分钟前
【开题答辩全过程】以 基于springboot的校园失物招领系统为例,包含答辩的问题和答案
java·spring boot·后端
寂静or沉默14 分钟前
2026最新Java岗位从P5-P7的成长面试进阶资源分享!
java·开发语言·面试
卓怡学长15 分钟前
m289在线交友系统
java·spring·tomcat·maven·intellij-idea·hibernate
zhglhy2 小时前
Java分库分表技术对比分析
java·分库分表
wuyikeer2 小时前
Spring Framework 中文官方文档
java·后端·spring
豆豆的java之旅2 小时前
软考中级软件设计师 数据结构详细知识点(含真题+练习题,可直接复习)
java·开发语言·数据结构
无心水2 小时前
Java时间处理封神篇:java.time全解析
java·开发语言·python·架构·localdate·java.time·java时间处理
wuyikeer2 小时前
Spring BOOT 启动参数
java·spring boot·后端
多看书少吃饭3 小时前
Vue + Java + Python 打造企业级 AI 知识库与任务分发系统(RAG架构全解析)
java·vue.js·笔记