案例

在后端开发中,树形结构数据的查询和处理是一个常见的需求,比如部门管理、分类目录展示等场景。接下来,我们以一个部门管理系统为例,详细介绍如何实现后端的树查询功能。

案例背景

假设我们正在开发一个公司的内部管理系统,其中部门管理模块需要展示部门之间的层级关系。部门数据以树形结构存储,每个部门都有自己的上级部门(根部门的上级部门 ID 为 0),我们需要实现接口查询出扁平结构的部门列表以及树形结构的部门数据。

表结构

CREATE TABLE `department_info` (`id` int NOT NULL AUTO_INCREMENT COMMENT '部门ID',`dep_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '部门名称',`level` int NOT NULL COMMENT '层级',`parent_id` int NOT NULL COMMENT '父ID,0表示根节点',PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci ROW_FORMAT=DYNAMIC;

在这里插入图片描述

在这个表结构中,id作为部门的唯一标识;dep_name存储部门名称;level表示部门在树形结构中的层级,方便后续对层级关系的处理;parent_id用于标识该部门的父部门,当parent_id为 0 时,表示该部门是根部门。

实体结构设计

在 Java 代码中,我们创建ParentDepartment实体类来映射数据库表中的数据,代码如下:

/*** 父子关系方案部门实体类* @TableName department_info*/
@TableName(value = "department_info")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ParentDepartment implements Serializable {// 序列化版本号private static final long serialVersionUID = 1L;/*** 部门ID*/@TableId(type = IdType.AUTO)private Integer id;/*** 部门名称*/private String depName;/*** 部门层级*/private Integer level;/*** 父部门ID(0表示根节点)*/private Integer parentId;/*** 子节点列表(非数据库字段)*/@TableField(exist = false)private List<ParentDepartment> children;/*** 是否为叶子节点(非数据库字段)*/@TableField(exist = false)private Boolean isLeaf;
}

实现思路

方法一:递归实现

递归是一种经典的处理树形结构数据的方法。基本思路是:首先从数据库中查询出所有的部门数据,然后找到所有根部门(即parent_id为 0 的部门),对于每个根部门,递归地查找它的子部门,将子部门添加到根部门的children列表中,直到所有部门都被正确添加到树形结构中。

    /*** 将扁平的部门列表转换为树形结构的部门列表。* 该方法会先找出所有根部门(即父部门ID为0的部门),* 然后递归构建每个根部门的子树。* * @param allDepartments 包含所有部门信息的扁平列表* @return 包含所有根部门及其子部门的树形结构列表*/public List<ParentDepartment> formatToTree(List<ParentDepartment> allDepartments) {// 用于存储所有根部门的列表List<ParentDepartment> rootDepartments = new ArrayList<>();// 遍历所有部门,找出父部门ID为0的根部门for (ParentDepartment department : allDepartments) {if (department.getParentId() == 0) {// 将根部门添加到根部门列表中rootDepartments.add(department);}}// 遍历所有根部门,为每个根部门构建子树for (ParentDepartment root : rootDepartments) {buildTree(root, allDepartments);}// 返回包含所有根部门及其子部门的树形结构列表return rootDepartments;}/*** 递归构建指定父部门的子树。* 该方法会遍历所有部门,找出当前父部门的所有子部门,* 并为每个子部门递归调用自身构建子树。* * @param parent 父部门对象* @param allDepartments 包含所有部门信息的扁平列表*/private void buildTree(ParentDepartment parent, List<ParentDepartment> allDepartments) {// 用于存储当前父部门的所有子部门的列表List<ParentDepartment> children = new ArrayList<>();// 遍历所有部门,找出当前父部门的子部门for (ParentDepartment department : allDepartments) {if (department.getParentId().equals(parent.getId())) {// 将子部门添加到子部门列表中children.add(department);// 递归构建子部门的子树buildTree(department, allDepartments);}}// 为父部门设置子部门列表parent.setChildren(children);}

方法二:hutool工具实现

Hutool 是一个功能丰富的 Java 工具类库,其中提供了方便的树形结构处理工具。使用 Hutool 实现树形结构查询更加简洁高效。

首先,需要在项目的pom.xml文件中引入 Hutool 依赖:

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.0</version>
</dependency>

实现树形结构查询的代码如下:

/*** 将扁平化的部门列表转换为树形结构* @param departments 扁平部门列表(需包含至少id, parentId, depName字段)* @return 树形结构列表(多个顶级节点构成森林结构)*/
public List<Tree<String>> formatToTreeSimple(List<ParentDepartment> departments) {// 1. 防御性编程:处理空输入if (CollUtil.isEmpty(departments)) {// 返回不可变空集合而非null,避免调用方NPEreturn Collections.emptyList();}// 2. 初始化树节点配置TreeNodeConfig config = new TreeNodeConfig();// 指定实体类字段与树节点属性的映射关系config.setIdKey("id");            // 节点ID对应实体类的id字段config.setParentIdKey("parentId");// 父节点ID对应实体类的parentId字段config.setChildrenKey("children");// 子节点集合的字段名称config.setNameKey("name");        // 节点显示名称对应实体类的depName字段// 3. 构建树形结构return TreeUtil.build(departments,   // 数据源集合"0",          // 根节点的父ID值(通常为0或null)config,       // 树配置(dept, tree) -> { // 自定义字段映射处理器// ---- 核心字段映射 ----// 设置节点ID(需转为String类型)tree.setId(dept.getId().toString());// 设置父节点ID(需转为String类型)tree.setParentId(dept.getParentId().toString());// 设置节点显示名称tree.setName(dept.getDepName());// ---- 扩展业务字段 ----// 添加部门层级信息tree.putExtra("level", dept.getLevel());// 添加叶子节点标记(根据children是否为空自动计算)tree.putExtra("isLeaf", dept.getIsLeaf());// 可继续添加其他业务字段...// tree.putExtra("manager", dept.getManagerName());});// 注:返回的List可能包含多个顶级节点(森林结构)// 通常业务中只有一个parentId="0"的根节点,可用get(0)获取
}

在这段代码中,我们先创建TreeNodeConfig对象,配置好 ID、父 ID、子节点列表以及名称对应的属性名。然后调用TreeUtil.build方法,传入部门数据列表、根节点 ID、配置对象以及一个函数式接口,在函数式接口中,我们将部门实体类的属性赋值给Tree对象,并可以根据业务需求添加额外的扩展字段。

在后端树查询的实现中,tree.putExtra("level", dept.getLevel());tree.putExtra("isLeaf", dept.getIsLeaf()); 这两行代码的作用是向树形结构的节点对象 tree 中添加额外的业务数据字段,也就是扩展字段 ,具体来说,它们的作用体现在以下几个方面:

  • 丰富节点信息:默认情况下,树形结构的节点可能只包含基础的标识信息(如节点 ID、父节点 ID、节点名称等)。通过添加levelisLeaf字段,可以让每个节点携带更多与业务相关的信息。比如level字段表示部门在树形结构中的层级,前端拿到数据后,就可以根据层级来设置不同的缩进样式,直观展示部门的层级关系;isLeaf字段表示该节点是否为叶子节点(即是否有子节点),这在前端进行交互操作时很有用,例如可以根据是否为叶子节点来决定是否显示展开 / 收缩按钮。
  • 方便业务处理:在实际业务中,很多操作需要依赖这些额外的信息。比如在权限管理中,可能不同层级的部门有不同的权限;在数据统计时,可能需要区分叶子节点和非叶子节点进行不同的计算。将这些字段直接附加到树形结构的节点中,在后续业务逻辑处理时,就无需再通过复杂的查询或计算来获取,提高开发效率。
  • 增强数据通用性:添加扩展字段使树形结构数据更具通用性和灵活性。即使当前业务不需要这些字段,未来如果有新的功能需求,比如添加部门层级相关的筛选功能,或者根据叶子节点状态进行特殊展示等,已经存在的扩展字段就能直接使用,而不需要对数据结构和代码进行大规模修改。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高效沟通04-RIDE说服模型

高效沟通专栏–组织运转的命脉与个人成功的基石 目录 1. RIDE模型的核心理念2. RIDE模型的应用场景3. RIDE模型使用步骤4. RIDE模型示例与练习4.1 应用RIDE模型:4.2 练习:你来试试!5. 总结RIDE模型是一种结构化的说服框架,旨在帮助你在沟通(尤其是书面沟通或需要清晰逻辑…

利用selenium获取网页数据,脚本加载慢问题的解决办法

问题&#xff1a;最近在写一个脚本去获取一个网站的数据&#xff0c;用到一个表格中的数据&#xff0c;条目是1000条&#xff0c;需要逐条去获取网站上对应的数据&#xff0c;遇到的问题是脚本运行后&#xff0c;很久才开始打开驱动浏览器。经过很多次尝试&#xff0c;主要原因…

Ubuntu查看本机代理的实操指南

快速确认代理状态的必要性在Ubuntu系统中&#xff0c;代理设置是跨境访问、企业内网连接、开发调试的重要配置。无论是排查网络卡顿、验证代理是否生效&#xff0c;还是确保特定应用走代理通道&#xff0c;快速查看当前代理状态都是关键步骤。图形界面查看&#xff0c;可视化操…

三格电子——双通道 CAN(FD)转以太网

【SG-CAN(FD)NET-210】 一、功能描述 CANFD 完全向下兼容 CAN &#xff0c;以下统称 CAN(FD) 。 SG-CAN(FD)NET-210 是一款用来把 CANFD 总线数据转为网口数据的设 备。 网口支持 TCP Sever 、 TCP Client 、 UDP Sever 、 UDP Client 四种模式。 可以通过软件配置…

【一起来学AI大模型】卷积神经网络(CNN):视觉识别的革命性架构

一、CNN的核心思想与生物启示 卷积神经网络&#xff08;Convolutional Neural Networks&#xff09;是受生物视觉皮层启发的深度学习架构&#xff0c;专门用于处理网格状拓扑数据&#xff08;如图像、视频、音频&#xff09;。其核心创新在于&#xff1a; 局部感受野&#xff…

创建和编辑Crontab的方法

计划任务&#xff0c;在 Linux 中一般使用Crontab&#xff0c;通过crontab命令&#xff0c;我们可以在固定的间隔时间执行指定的系统指令或 Shell 脚本。时间间隔的单位可以是分钟、小时、日、月、周及以上的任意组合。这个命令非常适合周期性的日志分析或数据备份等工作。 创建…

在职场中如何培养创新思维?

芯片研发人员&#xff0c;授权发明专利40&#xff0c;聊聊技术层面的创新&#xff0c; 创新的本质&#xff0c;是旧有知识的创造性组合&#xff0c; 不存在无中生有的创新&#xff0c; 你必须建立本领域的知识体系&#xff0c;对过往各种创新&#xff0c;烂熟于心&#xff0…

设备健康管理平台功能深度对比:中讯烛龙如何以预测性维护重构工业运维范式?

全球制造业因非计划停机每年损失超千亿美元​&#xff0c;而搭载预测性维护系统的企业&#xff0c;设备可用率可提升至99.8%​​。 在工业4.0与智能制造浪潮下&#xff0c;设备健康管理平台已从“可选工具”升级为“核心生产力工具”。面对市场上功能繁杂的解决方案&#xff0c…

YOLOv11 架构优化:提升目标检测性能

YOLOv11 作为目标检测领域的最新成果&#xff0c;其架构优化是提升性能的关键。本文将详细探讨 YOLOv11 的架构改进&#xff0c;以及这些改进如何帮助模型在实时应用中实现更高的准确性和效率。 一、架构改进 &#xff08;一&#xff09;C3K2 块 C3K2 块是对 CSP 块的增强&a…

特别放送:关于一个无法修复的系统级Bug

大家好&#xff0c;我是阿威。 熟悉我的朋友都知道&#xff0c;我的博客基本只聊三件事&#xff1a;代码、架构和偶尔的职业生涯吐槽。但今天&#xff0c;我想破个例。起因是上周熬夜排查一个线上问题&#xff0c;一个分布式系统&#xff0c;流量洪峰一来&#xff0c;某个下游…

云原生-集群管理

1.集群管理命令&#xff1a;a.如何管理集群&#xff1f;-kubectl是用于控制Kubernetes集群的命令行工具b.语法格式&#xff1a;-kubectl [command] [TYPE] [NAME] [flages]command:子命令&#xff0c;如create、get、descrbe、deletetype&#xff1a;资源类型&#xff…

基于Linux下的vscode c/c++开发环境搭建详细教程

vscode是文本编辑而非集成开发环境&#xff0c;需要经过配置才能在其上编译执行代码。本教程将具体详解在linux上配置Visual Studio Code使用GCC C 编译器&#xff08;g&#xff09;和GDB调试器的方法&#xff08;GCC是GNU 编译器集合&#xff0c;GDB则是 GNU调试器&#xff09…

【EGSR2025】材质+扩散模型+神经网络相关论文整理随笔

MatSwap: Light-aware material transfers in images 介绍任务&#xff1a;输入一张拍摄图像、示例材质纹理图像&#xff08;这里跟BRDF无关&#xff0c;通常我们讲到材质一般指的是SVBRDF&#xff0c;但是这里的材质指的只是纹理&#xff09;、用户为拍摄图像指定的遮罩区域&…

WebRTC 双向视频通话

WebRTC 双向视频通话 一、项目概述 WebRTC&#xff08;Web Real - Time Communication&#xff09;是一种支持浏览器之间进行实时通信的技术&#xff0c;它使得在网页上实现音视频通话、文件共享等功能变得更加容易。为了体验这个技术&#xff0c;所以我实现了webrtc - local…

Paimon lookup核心过程:分级查找、二分和缓存创建

LookupLevels LookupLevels 在 Paimon 中扮演着**“带缓存的、基于 Key 的数据查找引擎”**的角色。它的核心使命是&#xff1a;当需要根据主键&#xff08;Key&#xff09;查找某条数据时&#xff0c;能够高效地在 LSM-Tree 的多层&#xff08;Levels&#xff09;数据文件中定…

Ruby大会演讲实录:Baklib 如何用 AI 重构内容管理赛道

“2015 年成都 Ruby 大会时&#xff0c;我们还在做大数据项目&#xff1b;2025 年的今天&#xff0c;Baklib 已服务 800 多家企业。” 在 RubyConf China 2025 的演讲台上&#xff0c;Baklib 创始人Song以十年对比开篇&#xff0c;讲述了从技术爱好者到企业服务创业者的蜕变&am…

408第三季part2 - 计算机网络 - 传输层II

理解第一次和第二次握手是不能携带数据&#xff0c;只能消耗一个序号后面挥手也有第一次和第三次题目建立连接是1000&#xff0c;FIN挥手是5001&#xff0c;这两个是不会带数据的所以字节数范围是1001-50005000-10011 4000c再次理解还可以叫快速重传题目服务器想要100确认号客…

揭秘图像LLM:从像素到语言的智能转换

图像LLM是怎么工作 图像LLM(多模态大语言模型)的核心是将图像转化为语言模型能理解的“语言”,并与文本深度融合。以下结合CLIP、DALL-E、GPT-4V等主流模型,通过具体例子说明其工作机制: 一、图像→特征向量:从像素到“密码” 例子:识别“戴墨镜的猫” 视觉编码器提取…

十、K8s集群资源合理化分配

十、K8s集群资源合理化分配 文章目录 十、K8s集群资源合理化分配1、K8s 资源限制 ResourceQuota1.1 什么是ResourceQuota&#xff1f;1.2 ResourceQuota通常用于如下场景&#xff1a;1.3 基于租户和团队的资源限制1.4 基于命名空间的资源限制 2、K8s 资源限制 LimitRange2.1 设…

Android 13 设置界面会判断当前屏幕的大小,如果是大屏,则为左右屏显示

1.前言 在13.0的系统rom定制化开发中,在某些时候,在大屏设备中,设置新增了左右分屏的功能,就是 左边显示主菜单,右边显示一级菜单的功能,某些情况下不需要,接下来关闭这个功能 2.设置界面会判断当前屏幕的大小,如果是大屏,则为左右屏显示的核心类 packages/apps/Sett…