若依框架---数据转树状层级

news2025/6/4 1:06:39

👏作者简介:大家好,我是小童,Java开发工程师,CSDN博客博主,Java领域新星创作者
📕系列专栏:前端、Java、Java中间件大全、微信小程序、微信支付、若依框架、Spring全家桶
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
🍂博主正在努力完成2023计划中:以梦为马,扬帆起航,2023追梦人 

 

问题起源

最近在给公司的后台管理系统添加用户管理,部门管理,角色管理等等功能,设计到一个很重要的显示是,树状Tree来显示部门层级结构,如下图:

而数据库mysql中的数据是这样的:

是二维的,扁平的,一行一行的,准确的说,检索出来是一个条状的list,这个list通过parent_id有所关联。

那么,如何将其转换为树状层级结构呢?

通常由两种方式,一种是后台转换,在查询的时候,通过后台程序构造树状结构关联。一种是前端转换,通过javascript来将后端返回的列表list转换为一颗或多颗树。

 后台转换

逐层查找

我们先来看看如何通过后端查询来进行转换:

首先查询根部节点:

public List<Record> getDeptTree() {
	List<Record> orgs = Db.find("SELECT dept_id as id, dept_name as `label` FROM sys_dept WHERE parent_id = 0 ORDER by order_num ASC ");
    for (Record o : orgs) {
        buildChildren(o);
    }
    return orgs;    
}

 重点关注其中的方法 buildChildren(Record dept),这是一个递归函数:

private void buildChildren(Record org) {
    List<Record> children = getOrgChildren(org.getInt("id"));
    if (!children.isEmpty()) {
        for (Record r : children) {
            buildChildren(r);
        }
        org.set("children", children);
    }
}

private List<Record> getOrgChildren(Integer id) {
		List<Record> children = Db.find("SELECT dept_id as id, dept_name as `label` FROM sys_dept WHERE parent_id = ? ORDER by order_num ASC , id);
		return children;
	}

如此便将其数据从一行一行的数据表中构造为树状结构,如下所示:

[
    {
        "id": 100,
        "label": "若依科技",
        "children": [
            {
                "id": 101,
                "label": "深圳总公司",
                "children": [
                    {
                        "id": 103,
                        "label": "研发部门"
                    },
                    {
                        "id": 104,
                        "label": "市场部门"
                    },
                    {
                        "id": 105,
                        "label": "测试部门"
                    },
                    {
                        "id": 106,
                        "label": "财务部门"
                    },
                    {
                        "id": 107,
                        "label": "运维部门"
                    }
                ]
            },
            {
                "id": 102,
                "label": "长沙分公司",
                "children": [
                    {
                        "id": 108,
                        "label": "市场部门"
                    },
                    {
                        "id": 109,
                        "label": "财务部门"
                    }
                ]
            }
        ]
    }
]

上述算法是我们在自己的数据中重新实现的,

若依中的实现似乎是另一种方式。

 

检索全部数据算法构造

即一次性检索出全部的数据,然后通过算法来构造树状结构。若依中便是这样来实现的。

@Override
public List<TreeSelect> buildDeptTreeSelect(List<SysDept> depts)
{
    List<SysDept> deptTrees = buildDeptTree(depts);
    return deptTrees.stream().map(TreeSelect::new).collect(Collectors.toList());
}

/**
     * 构建前端所需要树结构
     * 
     * @param depts 部门列表
     * @return 树结构列表
     */
    @Override
    public List<SysDept> buildDeptTree(List<SysDept> depts)
    {
        List<SysDept> returnList = new ArrayList<SysDept>();
        List<Long> tempList = new ArrayList<Long>();
        for (SysDept dept : depts)
        {
            tempList.add(dept.getDeptId());
        }
        for (Iterator<SysDept> iterator = depts.iterator(); iterator.hasNext();)
        {
            SysDept dept = (SysDept) iterator.next();
            // 如果是顶级节点, 遍历该父节点的所有子节点
            if (!tempList.contains(dept.getParentId()))
            {
                recursionFn(depts, dept);
                returnList.add(dept);
            }
        }
        if (returnList.isEmpty())
        {
            returnList = depts;
        }
        return returnList;
    }

其中的判断语句:

if (!tempList.contains(dept.getParentId())) { ...将节点添加到返回列表中 }

判断的是,当前列表中是否有其父节点,如果某有,说明该节点是最顶级的节点。

再来看其中的递归方法 recursionFn(depts, dept)

/**
     * 递归列表
     */
    private void recursionFn(List<SysDept> list, SysDept t)
    {
        // 得到子节点列表
        List<SysDept> childList = getChildList(list, t);
        t.setChildren(childList);
        for (SysDept tChild : childList)
        {
            if (hasChild(list, tChild))
            {
                recursionFn(list, tChild);
            }
        }
    }

这里的list总是完整检索的数据,而参数t表示以t为父节点的所有子节点。

获取节点子节点的函数:

/**
     * 得到子节点列表
     */
    private List<SysDept> getChildList(List<SysDept> list, SysDept t)
    {
        List<SysDept> tlist = new ArrayList<SysDept>();
        Iterator<SysDept> it = list.iterator();
        while (it.hasNext())
        {
            SysDept n = (SysDept) it.next();
            if (StringUtils.isNotNull(n.getParentId()) && n.getParentId().longValue() == t.getDeptId().longValue())
            {
                tlist.add(n);
            }
        }
        return tlist;
    }

总结

两种方式各有优劣,第二种方式数据库检索次数少,每次检索子节点不需要向数据库发起请求,但是其需要满足能够一次性查询出所有的数据;第一种方式数据库查询次数多,但是如果仅知道一个父节点id通过这种方式来构造数据比较直观,尤其是当你的数据库层级结构设计上没有ancestors字段时。

专栏:若依框架

 

                                                                       关注公众号,领取资料。 

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

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

相关文章

Node=>Express路由 学习2

1.概念 Express路由指的是客户端的请求与服务器处理函数之间的映射关系 Express路由由三部分组成 请求类型 请求URL地址 处理函数 app.METHOD ( PATH , HANDLER )根据定义的先后顺序进行匹配 请求类型和请求的URl同时匹配成功才会调用相应的处理函数 简单用法 2.模块化路由 为了…

《里奥哈酒友记》 | 里奥哈的历史—品鉴瑞格尔侯爵葡萄酒

2022年末&#xff0c;里奥哈大使组合怪怪和思羽完成了里奥哈线上活动两大“壮举”&#xff0c;10期《里奥哈酒友记》系列视频和40集《美酒之乡——里奥哈》有声专辑&#xff0c;吸引了许多葡萄酒资深爱好者的目光&#xff0c;也成功地让更多的人了解到里奥哈。由里奥哈推广大使…

粘包拆包问题

什么是粘包/拆包 TCP是个“流”式的协议&#xff0c;会根据TCP缓冲区的实际情况进行包的划分。在业务层面认为一个完整的包&#xff0c;可能会被TCP拆分成多个小包进行发送&#xff0c;也可能把多个小的包封装成一个大的数据包进行发送 粘包拆包问题解决方法 由于TCP协议底层…

电脑分盘怎么分?分盘详细教程来了,图文教学

电脑作为小伙伴日常生活使用的工具&#xff0c;很多事情都需要使用电脑来进行处理。虽然小伙伴使用电脑比较多&#xff0c;但是还是有不少的小伙伴不知道电脑分盘怎么分&#xff1f;其实电脑分盘很简单&#xff0c;下面小编就以图文教学的方式&#xff0c;详细的向小伙伴介绍电…

Python:迷宫

题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出即可。 X 星球的一处迷宫游乐场建在某个小山坡上。它是由 1010 相互连通的小房间组成的。 房间的地板上写着一个很大的字母。我们假设玩家是面朝上坡的方向站立&#xff0…

【Linux系统编程】01:命令行解析

命令行解析 1.getopt调用介绍 头文件&#xff1a;<unistd.h> 原型&#xff1a;int getopt(int argc, char * const argv[], const char *optstring); argc、argv&#xff1a;由main函数的参数直接传递而来optstring&#xff1a;一个包含所有合法可选字符组成的字符串 …

超高精度PID调节器的特殊功能(3)——变送输出(转发)功能及其应用

摘要&#xff1a;变送输出是高级PID控制器的一项重要扩展功能&#xff0c;可用于多区控制、串级控制、比值控制和差值控制以及数据采集及记录。为展示变送输出功能的强大作用&#xff0c;本文主要针对超高精度VPC 2021系列PID控制器&#xff0c;介绍了变送输出的具体功能、参数…

【FPGA】Verilog:组合电路 | 3—8译码器 | 编码器 | 74LS148

前言&#xff1a;本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例&#xff1a;编码/译码器的应用 ​ 功能特性&#xff1a; 采用 Xilinx Artix-7 XC7A35T芯片 配置方式&#xff1a;USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度 存储器&…

部门新来了个软件测试工程师,看着一副毛头小子的样儿,哪想到是新一代卷王...

内卷&#xff0c;是现在热度非常高的一个词汇&#xff0c;随着热度不断攀升&#xff0c;隐隐到了“万物皆可卷”的程度。 在程序员职场上&#xff0c;什么样的人最让人反感呢? 是技术不好的人吗?并不是。技术不好的同事&#xff0c;我们可以帮他。 是技术太强的人吗?也不…

【精彩点评】正确理解区块链能源消耗的内涵以及对绿色区块链的探索

发表时间&#xff1a;2022年4月13日 信息来源&#xff1a;bsvblockchain.org 为了理解区块链技术的工作原理并确定如何更好地对其加以利用&#xff0c;就区块链技术提出疑问是不可避免的。也许你正在被区块链的能源效率这个问题所困扰。 经常有人说&#xff0c;一些区块链网络…

Spring MVC 之获取参数(对象、JSON格式数据、URL地址参数、文件、Cookie)

文章目录1. 获取单个参数2. 获取多个参数3. 获取对象4. 后端参数重命名 RequestParam5. 接收 JSON 格式的数据 RequestBody6. 从 URL 地址中获取参数 PathVariable7. 上传文件 RequestPart8. 获取Cookie (CookieValue)/Session/header8.1 获取 Request 和 Response 对象8.2 获取…

【GD32F427开发板试用】SysTick定时器的ms中断,us计时

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;远小刘 1、系统滴答定时器 参考资料《Cortex M3与M4权威指南.pdf》中的9.5 The SysTick timer小节&#xff0c;里面详细讲解了 1、为什么要用…

一分钟教会使用ChatGPT,保姆级ChatGPT教程

最近ChatGPT大火&#xff01;微软退出首款ChatGPT搜索引擎&#xff0c;阿里等国内巨头也纷纷爆出自家产品&#xff0c;一夜之间&#xff0c;全球最大的科技公司仿佛都回到了自己年轻时的样子&#xff01; 然而&#xff0c;ChatGPT这么火&#xff0c;这么好玩的东西&#xff0c…

Knowledgeowl VS Baklib:你需要知道的一切!

KnowledgeOwl 和 Baklib都是知识库软件的示例。您可以使用每个平台为用户创建自己的知识库和托管文档。每个平台都允许您从同一帐户创建多个知识库&#xff0c;并且可以为用户提供内部和外部知识库。什么是知识库&#xff1f;知识库是一个集中的信息存储库&#xff0c;旨在为无…

Koa2-项目中的基本应用

文章目录安装配置koa2配置nodemon,热更新我们的项目中间件什么是中间件&#x1f47b;洋葱模型路由中间件连接数据库 - mysql后端允许跨域处理请求getpostputdelete后续会继续更新安装配置koa2 &#x1f47b;安装 koa2 npm i koa2 -s&#x1f47b;在package.json 配置,当然是在…

用LCR测试仪测试无线充电系统中的线圈

宽阻抗范围用来表征电感和质量因数– 高精度 DCR 测量– 制造环节快速测量– 大量夹具可供选择智能终端上不断增加新功能&#xff0c;电池寿命成为用户最头痛的问题之一。相比便携式电源和电缆供电而言&#xff0c;无线充电技术因其方便性和多功能性获得了很大的关注&#xff0…

MySQL —— 表的增删查改(一)

目录 表的增删查改&#xff08;一&#xff09; 一、create 1. 单行数据全列插入 2. 多行数据指定列插入 3. 插入否则更新 4. 替换 二、Retrieve 1. select列 1. 全列查询 2. 指定列查询 3. 查询字段为表达式 4. 为查询结果指定别名 5. 结果去重 2. where条件 1…

辞旧岁立新年 | 展望前端工程师的2023

「InfoQ 年度技术盘点与展望」是由InfoQ主办的年度技术盘点与展望项目&#xff0c;是 InfoQ 全年最重要的内容选题之一。 近日&#xff0c;字节跳动云原生 PaaS 资深前端工程师黄健受邀参与&#xff0c;分享主题为 《展望前端研发工程师的 2023》 。本文整理自黄健于直播中分享…

中国区注册OpenAI账号,试用ChatGPT指南

OpenAI最近推出ChatGPT&#xff0c;但国内&#xff08;包括香港&#xff09;并不支持OpenAI账号注册&#xff0c;多数会提示&#xff1a; OpenAI’s services are not available in your country. 前期准备 科学上网&#xff0c;最好是美国IP&#xff08;可以购买v屁n&…

(三)循环神经网络RNN之LSTM和GRU

LSTM介绍 LSTM(Long Short Term Memory)是 Hochreater 和 Schmidhuber 在 1997 年提出的一种网络结构&#xff0c;尽管该模型在序列建模上的特性非常突出&#xff0c;但由于当时正是神经网络的下坡期&#xff0c;没有能够引起学术界足够的重视。随着深度学习逐渐发展&#xff…