项目实战——获取树形结构

news2024/9/26 5:20:59

获取树形结构

  • 一、背景介绍
  • 二、 思路和方案
    • 方案一:使用递归查询的方式并构建树形结构
    • 方案二:使用临时表的方式构建树形结构
    • 使用临时表的优缺点
  • 三、过程
    • 项目案例
      • 核心代码
  • 四、总结
  • 五、升华

一、背景介绍

我们在开发中时常会遇到需要用到树形结构这种表示层级关系的结构来表示数据,层级关系明确用户能够很直观的理解彼此的关系。
例如:
在这里插入图片描述
本篇博客主要是总结从一张字段具有父子关系的表中查询并将其转换成树形结构通常需要怎么做,具体步骤是什么?以此来解决后序需要类似的问题如何快速实现或者是有思路,将经历转变为自己的经验。

二、 思路和方案

目前一共有两种实现的方案,方案一:使用递归查询的方式并构建树形结构;方案二:使用临时表的方式构建树形结构。

方案一:使用递归查询的方式并构建树形结构

通过递归查询的方式,从根节点开始递归查询所有子节点,然后将他们组成一个树形结构。

宏观步骤:

  1. 通过一次查询获取整个表中的数据。
  2. 再从查询出的数据中筛选出根节点。
  3. 从根节点开始查询所有子节点,再对于每个子节点,在递归查询他们所有子节点
    并将它们组成一个子树。
  4. 将所有子树组成一个完整的属性结构。

方案二:使用临时表的方式构建树形结构

下面是列举的一个示例,起到提供思路的目的。这块儿内容并没有进行实践

宏观步骤:
1.创建临时表temp_tree,用来存储树形结构的各个节点,包括节点id、节点名称、父节点id和层级等信息。

CREATE TEMPORARY TABLE temp_tree (
  id INT PRIMARY KEY,
  name VARCHAR(255),
  parent_id INT,
  level INT
);

2.向临时表中插入树形结构的各个节点信息。

INSERT INTO temp_tree (id, name, parent_id, level)
SELECT id, name, parent_id, level
FROM your_table
ORDER BY parent_id, id;

注意这里的查询语句需要按照父节点id和节点id进行排序,以保证树形结构的正确性。

3.通过递归查询的方式,构造树形结构的节点之间的关系,并将结果存储在一个数组或者集合中。

List<Node> nodeList = new ArrayList<>();

// 递归查询,构造节点之间的关系
public void buildTree(List<Node> nodeList, int parentId, int level) {
    for (Node node : temp_tree) {
        if (node.getParentId() == parentId) {
            node.setLevel(level);
            nodeList.add(node);
            buildTree(nodeList, node.getId(), level + 1);
        }
    }
}

// 构建树形结构
buildTree(nodeList, 0, 0);

4.根据构造出来的节点关系,生成树形结构的数据行。

public List<Map<String, Object>> generateTreeData(List<Node> nodeList) {
    List<Map<String, Object>> treeData = new ArrayList<>();
    for (Node node : nodeList) {
        Map<String, Object> data = new HashMap<>();
        data.put("id", node.getId());
        data.put("name", node.getName());
        data.put("level", node.getLevel());
        if (node.getParentId() != 0) {
            data.put("parentId", node.getParentId());
        }
        treeData.add(data);
    }
    return treeData;
}

// 生成树形结构数据行
List<Map<String, Object>> treeData = generateTreeData(nodeList);

注意这里生成的树形结构数据行可以根据实际需要进行定制,比如可以增加其他属性,比如节点状态、节点类型等等。

使用临时表的优缺点

  1. 使用临时表的主要优势是它能够在数据库中更加灵活地处理数据,因此适用于一些需要进行更加复杂操作的情况,如对树形结构进行排序、过滤等操作。
  2. 灵活性:使用临时表实现可以将数据加载到内存中,这使得在程序中对树形结构进行操作时更加灵活,尽管临时表在构建时需要一定的时间和资源,但在数据量较大时,在临时表中,数据已经存储在内存中,查询和修改速度更快,能够更好地支持大规模数据的处理。

同时,当你需要对数据进行分页处理时,使用方案一可能会比较麻烦,因为你需要对整个数据集进行递归并处理出树形结构,然后再进行分页操作。而使用临时表,你可以在数据库中将数据按照分页条件先筛选出来,然后再进行递归并处理出树形结构。

临时表的缺点是需要占用更多的存储空间,而且实现起来相对比较复杂,需要编写 SQL 查询语句。如果数据量较大或者需要对树形结构进行复杂的操作,使用临时表可能更加合适。

三、过程

目前采取的是 方案一:使用递归查询的方式并构建树形结构
在程序中进行递归构建树形结构,并且在此之前已经将数据全部取出,那么使用方案一也许会更加简单和高效。因为你无需再将数据导入到临时表中。使用临时表的主要优势是它能够在数据库中更加灵活地处理数据,因此适用于一些需要进行更加复杂操作的情况,如对树形结构进行排序、过滤等操作。

项目案例

目前是需要从一张字段具有父子关系的表中查询并将其转换成树形结构。

表结构:p_id表示父节点,p_id为0表示为根节点
在这里插入图片描述
前端渲染的结果

在这里插入图片描述

核心代码

树形结构体中的核心内容

public class ChapterTreeInfo implements Serializable {

    /**
     * 主键
     */
    @JsonSerialize(using = com.fasterxml.jackson.databind.ser.std.ToStringSerializer.class)
    private BigInteger id;
    /**
     * 父章节id
     */
    private BigInteger p_id;
    /**
     * 章节目录排序
     */
    private Integer order;
    /**
     * 章节id
     */
    private String chapter_id;
    /**
     * 章节名称
     */
    private String chapter_title;
    /**
     * 执行时间
     */
    private Integer limit_time;
   
    /**
     * 活动id
     */
    private String active_id;

    private List<ChapterTreeInfo> children;
    
 	// getter and setter methods

核心方法

//1.通过课程id查询所有的章节信息
        List<ArproChapterInfo> arproChapterInfos = arproChapterInfoMapper.queryChapterInfo(courseId);
        //将需要的数据导入到准备好的ChapterTreeInfo结构中,为后序的递归做准备
        List<ChapterTreeInfo> chapterTreeInfos = dataProcessing(arproChapterInfos);

        //容器,用于表示整体的树形结构
        List<ChapterTreeInfo> chapterTreeInfoList=new ArrayList<>();
        //找出父节点,并为其添加子节点
        for (ChapterTreeInfo chapterTree:chapterTreeInfos) {
            //说明此节点是最初的父节点,为他添加子节点
            if(chapterTree.getP_id()==null||chapterTree.getP_id().intValue()==0){
                //找到该父节点下的所有子节点(以及子节点下的子节点...)
                ChapterTreeInfo chapterTreeInfo=findChildren(chapterTree,chapterTreeInfos);
                //为该父节点添加子节点
                chapterTreeInfoList.add(chapterTreeInfo);
            }
        }
        return chapterTreeInfoList;

dataProcessing方法将将查询出来的数据更换到树形结构中

/**
     * 将字典数据遍历放入chapterTreeInfos中
     *
     * @param arproChapterInfoList
     * @return
     */
    public static List<ChapterTreeInfo> dataProcessing(List<ArproChapterInfo> arproChapterInfoList) {
        //将entity转为自定义的model,有两种方式一种方式是进行for循环添加,一种是使用stream流进行添加,这里选择使用stream流中的peek操作允许你访问流中的每个元素,并且可以对每个元素进行一些操作
        List<ChapterTreeInfo> chapterTreeInfos=new ArrayList<>();
        arproChapterInfoList.stream().peek(chapterLeaf->{
            ChapterTreeInfo chapterTree =new ChapterTreeInfo();
            //获取节点的父id
            chapterTree.setP_id(chapterLeaf.getpId());
            //获取节点的id
            chapterTree.setChapter_id(chapterLeaf.getChapterId());
            //获取节点名字
            chapterTree.setChapter_title(chapterLeaf.getChapterTitle());
            //获取节点时间
            chapterTree.setLimit_time(chapterLeaf.getLimitTime());
            //获取节点内容
            chapterTree.setContents(chapterLeaf.getContents());
            //获取节点的信息
            chapterTree.setType(chapterLeaf.getType());
           //将节点信息存入到树形结构中
            chapterTreeInfos.add(chapterTree);
        }).collect(Collectors.toList());
        return chapterTreeInfos;
}

findChildren寻找根节点的子节点,进行递归

/**
     * 递归
     *
     * @param
     * @param chapterTreeModels
     * @return ChapterTreeModel
     */
    public static ChapterTreeInfo findChildren(ChapterTreeInfo chapterTreeModel, List<ChapterTreeInfo> chapterTreeModels) {

        for (ChapterTreeInfo chapterTree:chapterTreeModels) {
            //判断该节点的父节点id是否等于传递进来的节点的id(表名传进来的节点具有子节点)
            if (chapterTreeModel.getId().equals(chapterTree.getP_id())) {
                if (chapterTreeModel.getChildren()==null){
                    chapterTreeModel.setChildren(new ArrayList<>());
                }
                //递归查询当前节点是否还有子节点,直到没有子节点那就就将当前节点
                chapterTreeModel.getChildren().add(findChildren(chapterTree,chapterTreeModels));
            }
        }
        return chapterTreeModel;
     }

以上就是方案一的核心代码。

四、总结

  1. 通过这次博客总结对于查询树形结构有了更深的熟悉度和常用思路。
  2. 另外对于获取树形结构的另外一种方式,创建临时表的方式也有了一定的了解。以至于自己下一次遇到类似的问题多了一个思考的维度。
  3. 对于在程序中进行递归还是在数据库中递归,也有了这方面的了解,以及数据库实现递归的方式,如SQL 查询语句使用了 WITH RECURSIVE 关键字,实现了递归查询。

五、升华

  1. 对于解决问题的方式,不止一种,需要有意识的想到要去找多种的实现方式,进行对比选择一种复合当前的实现方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/495166.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

1分钟学会、3分钟上手、5分钟应用,快速上手责任链框架详解 | 京东云技术团队

作者&#xff1a;京东物流 覃玉杰 1. pie 简介 责任链模式是开发过程中常用的一种设计模式&#xff0c;在SpringMVC、Netty等许多框架中均有实现。我们日常的开发中如果要使用责任链模式&#xff0c;通常需要自己来实现&#xff0c;但自己临时实现的责任链既不通用&#xff0…

解决安装nrm,执行nrm ls时出现的const open=require(‘open’)问题

最开始安装的淘宝镜像源为npm config set registryhttps ://registry.npm.taobao.org/&#xff0c;后来看到镜像源变了&#xff0c;就换了下面的&#xff0c; 下载新的npm淘宝镜像资源包npm config set registry http://registry.npmmirror.com 查看&#xff0c;安装成功&…

【c语言】字符串的基本概念 | 字符串存储原理

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ …

Spring常用注解总结

目录 一、前言1、xml和注解的最佳实践&#xff1a;2、使用注解唯一需要注意的就是&#xff0c;必须开启注解的支持&#xff1a; 二、Spring的常用注解1、给容器中注入组件2、注入bean的注解3、JsonIgnore4、初始化和销毁方法5、Java配置类相关注解6、切面&#xff08;AOP&#…

DNS资源记录详解

资源记录&#xff08;resourse record&#xff09;就是域名服务器保存的记录&#xff0c;也是解析器请求的内容&#xff0c;资源记录保存在zone文件中。域&#xff08;domain&#xff09;&#xff1a;以 www.baidu.com 为例&#xff0c;com是一个域。baidu.com是一个域&#xf…

Selenium:三种等待方式

目录 一、显示等待 二、隐式等待 三、强制等待 UI自动化测试&#xff0c;大多都是通过定位页面元素来模拟实际的生产场景操作。但在编写自动化测试脚本中&#xff0c;经常出现元素定位不到的情况&#xff0c;究其原因&#xff0c;无非两种情况&#xff1a;1、有frame&#x…

Unity 光照

\\\\\\\ Unity烘焙&#xff08;Baking&#xff09;是指将场景中的动态光照转换为静态贴图。在烘焙过程中&#xff0c;Unity会将场景中的光源、材质和对象等信息计算出来&#xff0c;并存储为贴图。当玩家进入场景时&#xff0c;Unity只需要读取这些预计算好的贴图或者数据文件&…

2023年计算机视觉与模式识别国际会议(CCVPR 2023)

会议简介 Brief Introduction 2023年计算机视觉与模式识别国际会议(CCVPR 2023) 会议时间&#xff1a;2023年9月15日-17日 召开地点&#xff1a;英国牛津 大会官网&#xff1a;www.ccvpr.org 计算机视觉技术与模式识别是现代科学中备受关注的热点技术&#xff0c;它的革新对各行…

Monorepo开发策略详解

目录 一&#xff1a;什么是 Monorepo&#xff1f; 二&#xff1a;Monorepo 和其他结构的区别&#xff1a; 三&#xff1a;Monorepo的优缺点 3.1.优点 3.2.缺点 四&#xff1a;如何使用Monorepo 一&#xff1a;什么是 Monorepo&#xff1f; Monorepo 是一种将多个项目存放…

【iOS】—— RunLoop初学

RunLoop 文章目录 RunLoopRunLoop简介RunLoop基本使用Runloop伪代码Runloop模型图 Runloop对象Runloop对象的获取_CFRunLoopGet0方法 RunLoop的相关类RunLoop相关类的实现CFRunLoopRefCFRunLoopModeRef五种运行模式CommonModes什么是Mode Item&#xff1f;Mode到底包含哪些类型…

【SWAT水文模型】SWAT水文模型建立及应用第四期: 气象数据的准备(中国区域高精度同化气象站CMADS)

SWAT水文模型建立及应用&#xff1a; 气象数据的准备 1 简介2 气象数据的准备&#xff08;中国区域高精度同化气象站CMADS&#xff09;2.1 数据说明2.2 数据下载 3 CMADS 数据集SWAT子集使用说明3.1 SWAT2009版本3.2 SWAT2012版本 参考 本博客主要介绍气象数据的准备&#xff0…

HulaCWMS呼啦企业网站管理系统 v3.0.4

源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/87630654 HulaCWMS(呼啦企业网站管理系统)是基于ThinkPHP5框架开发&#xff0c;安全高效&#xff0c;包括ThinkPHP5的所有特性。专注于企业、政府单位网站建设&#xff0c;以免费开源的方式&#xff0c;帮…

python - 模块使用详解

前言 Python有非常强大的第三方库&#xff0c;也有非常多的内置模块帮助开发人员实现某些功能&#xff0c;无需开发人员自己造轮子。本文介绍Python的模块。 什么是模块 模块简单来说就是一系列功能的集合体&#xff0c;如果将程序的开发比喻成拼图&#xff0c;模块就是各种…

读懂海尔智家大脑:深度体验的本质是深度生活

了解科技行业的读者&#xff0c;应该都对“大脑”这个名词不陌生。 “黑灯工厂”里指挥生产的“工业大脑”&#xff0c;繁忙机场里运筹帷幄的“航空大脑”&#xff0c;还有智慧城市建设的灵魂“城市大脑”…… 如果家也有一颗总揽全局的大脑&#xff0c;生活会发生什么改变呢&a…

SuperMap GIS基础产品三维GIS FAQ集锦(2)

SuperMap GIS基础产品三维GIS FAQ集锦&#xff08;2&#xff09; 【WebGL】桌面对三维缓存设置了最大最小可见高度&#xff0c;在iServer发布三维服务并进行预览是可以看到该效果的&#xff0c;但在前端代码打开该服务&#xff0c;最大最小可见高度效果丢失&#xff0c;请问怎…

Makefile零基础教学(一)初识makefile

从这篇文章开始就开始进入 Makefile 的零基础教程&#xff0c;相信只要看了本教程的都可以对 Makefile 有一个清晰的理解和正确的运用。那么现在就开始我们的 Makefile 学习之路。 文章目录 一、什么是 Makefile&#xff0c;优点&#xff1f;二、什么是 make, 为什么使用make?…

可拓展哈希

可拓展哈希 借CMU 15445的ppt截图来说明问题。 我们传统静态hash的过程是hash函数后直接将值存入对应的bucket&#xff0c;但是在可扩展hash中&#xff0c;得查询Directory&#xff08;左&#xff09;&#xff0c;存入directory指向的bucket&#xff08;右&#xff09;。 下面…

linux线程池、基于线程池的单例模式、读者写者问题

线程池&单例模式 一、什么是线程池二、设计思路三、代码实现四、基于线程池的单例模式4.1 懒汉模式设计思路 五、读者写者问题及读写锁 一、什么是线程池 线程池: 一种线程使用模式。线程过多会带来调度开销&#xff0c;进而影响缓存局部性和整体性能。而线程池维护着多个…

flashback database 深入学习

一.FlashbackDatabase 和 Restore Points 说明 OracleFlashback Database and restore points are related data protectionfeatures that allow you to rewind data back in time tocorrect any problems caused by logical data corruption or user errors within adesignate…

gopher 初探

文章目录 gopher协议简介发送GET请求curlgopher gopher发POST请求 gopher协议 简介 Gopher是Internet上一个非常有名的信息查找系统&#xff0c;它将Internet上的文件组织成某种索引&#xff0c;很方便地将用户从Internet的一处带到另一处。在WWW出现之前&#xff0c;Gopher是…