当前位置: 首页 > news >正文

SpringBoot + MyBatis-Plus构建树形结构的几种方式

1. 树形结构

树形结构,是指:数据元素之间的关系像一颗树的数据结构。由树根延伸出多个树杈

在这里插入图片描述

它具有以下特点:

  • 每个节点都只有有限个子节点或无子节点;
  • 没有父节点的节点称为根节点;
  • 每一个非根节点有且只有一个父节点;
  • 除了根节点外,每个子节点可以分为多个不相交的子树;
  • 树里面没有环路(cycle)

2. 常见问题

 在实际开发中,很多数据都是树形结构,例如:地区、页面上的菜单、上下级关系的组织等等,这时就需要我们从数据源中读取到数据,通过某些方式拼成树形结构 然后再给前端展示。对于一些不经常变化且使用频繁的数据,可以考虑将拼好的树形结构数据放入缓存,每次用的时候直接读取出来就可以使用。

3. 准备环境

springboot: 2.6.0
mysql: 5.7

CREATE TABLE `t_region` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`name` varchar(100) DEFAULT NULL,`region_type` varchar(255) DEFAULT NULL,`parent_id` bigint(20) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;INSERT into t_region(name, region_type, parent_id) VALUES('山西省', 'province', 0);
INSERT into t_region(name, region_type, parent_id) VALUES('临汾市', 'city', 1);
INSERT into t_region(name, region_type, parent_id) VALUES('尧都区', 'district', 2);INSERT into t_region(name, region_type, parent_id) VALUES('北京', 'province', 0);
INSERT into t_region(name, region_type, parent_id) VALUES('北京市', 'city', 4);
INSERT into t_region(name, region_type, parent_id) VALUES('朝阳区', 'district', 5);INSERT into t_region(name, region_type, parent_id) VALUES('太原市', 'city', 1);
INSERT into t_region(name, region_type, parent_id) VALUES('小店区', 'district', 7);

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>spring-test</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.0</version><relativePath/></parent><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.10</version><scope>provided</scope></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.1.10</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- druid依赖 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.0</version></dependency></dependencies>
</project>
spring:main:allow-circular-references: truedatasource: #定义数据源#127.0.0.1为本机测试的ip,3306是mysql的端口号。serverTimezone是定义时区,照抄就好,mysql高版本需要定义这些东西#useSSL也是某些高版本mysql需要问有没有用SSL连接url: jdbc:mysql://192.168.1.141:3306/db_user?serverTimezone=GMT%2B8&useSSL=FALSEusername: root  #数据库用户名,root为管理员password: 123456 #该数据库用户的密码# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource
mybatis-plus:# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)mapper-locations: classpath:mapper/*.xml# 以下配置均有默认值,可以不设置global-config:db-config:#主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";id-type: auto#数据库类型db-type: MYSQLconfiguration:# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射map-underscore-to-camel-case: true# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段call-setters-on-nulls: true# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

表对应的实体类

@Data
@TableName(value = "t_region")
public class Region {@TableId(type = IdType.AUTO)private Long id;/*** 名称*/private String name;/*** 类型*/private String regionType;/*** 父id*/private Long parentId;
}

返回给前端的实体类

@Data
public class RegionVO {private Long id;/*** 名称*/private String name;/*** 类型*/private String regionType;/*** 父id*/private Long parentId;private List<RegionVO> children;
}

4.实现方式

1.基于xml

RegionMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.example.mapper.RegionMapper"><resultMap id="regionMap" type="org.example.dto.RegionVO"><id property="id" column="id"></id><result property="name" column="name"></result><result property="regionType" column="region_type"></result><result property="parentId" column="parent_id"></result><collection property="children" ofType="org.example.dto.RegionVO" javaType="java.util.List"column="id" select="getById"></collection></resultMap><select id="getById" resultMap="regionMap" parameterType="map">SELECT*FROMt_regionwhere parent_id=#{id}</select><select id="getAll" resultMap="regionMap">SELECT*FROMt_region where parent_id = 0</select></mapper>

RegionMapper

@Mapper
public interface RegionMapper extends BaseMapper<Region> {List<RegionVO> getAll();
}

RegionService

public interface RegionService extends IService<Region> {List<RegionVO> getAll();
}

RegionServiceImpl

@Service
public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> implements RegionService {@Overridepublic List<RegionVO> getAll(){return baseMapper.getAll();}
}

这种方式按照上边添加的数据量(8条)共执行了9次查询

在这里插入图片描述

2.LambdaQueryWrapper

RegionServiceImpl添加如下代码

 @Overridepublic List<RegionVO> getAllWrapper(){LambdaQueryWrapper<Region> wrapper = new LambdaQueryWrapper<>();wrapper.eq(Region::getParentId, 0);//查询根级List<Region> regions = baseMapper.selectList(wrapper);List<RegionVO> list = regions.stream().map(p -> {RegionVO obj = new RegionVO();BeanUtils.copyProperties(p, obj);return obj;}).collect(Collectors.toList());list.forEach(this::getChildren);return list;}private void getChildren(RegionVO item){LambdaQueryWrapper<Region> wrapper = new LambdaQueryWrapper<>();wrapper.eq(Region::getParentId, item.getId());//根据parentId查询List<Region> list = baseMapper.selectList(wrapper);List<RegionVO> voList = list.stream().map(p -> {RegionVO vo = new RegionVO();BeanUtils.copyProperties(p, vo);return vo;}).collect(Collectors.toList());//写入到childrenitem.setChildren(voList);//如果children不为空,继续往下找if (!CollectionUtils.isEmpty(voList)) {voList.forEach(this::getChildren);}}

这种方式按照上边添加的数据量(8条)共执行了9次查询

在这里插入图片描述

3.递归方法

RegionServiceImpl添加如下代码

 @Override
public  List<RegionVO> build(){//一次把所有的数据都查出来List<Region> regions = baseMapper.selectList(null);List<RegionVO> allList = regions.stream().map(p -> {RegionVO vo = new RegionVO();BeanUtils.copyProperties(p, vo);return vo;}).collect(Collectors.toList());//指定根节点的parentIdreturn buildChildren(0L, allList);
}private List<RegionVO> buildChildren(Long parentId, List<RegionVO> allList){List<RegionVO> voList = new ArrayList<>();for (RegionVO item : allList) {//如果相等if (Objects.equals(item.getParentId(), parentId)) {//递归,自己调自己item.setChildren(buildChildren(item.getId(), allList));voList.add(item);}}return voList;
}

这种就不必说了,一次查询所有数据出来,一共执行一次查询

在这里插入图片描述

4.总结

 查询方式有很多,应该使用哪种需要猿们结合具体情况选择。

第一种情况:当整体数据量特别大 层级不深 需要按照某个根节点查询时,推荐使用第一、二种方式。
第二种情况:当需要查询整个树时,推荐使用第三种方式。

相关文章:

SpringBoot + MyBatis-Plus构建树形结构的几种方式

1. 树形结构 树形结构&#xff0c;是指&#xff1a;数据元素之间的关系像一颗树的数据结构。由树根延伸出多个树杈 它具有以下特点&#xff1a; 每个节点都只有有限个子节点或无子节点&#xff1b;没有父节点的节点称为根节点&#xff1b;每一个非根节点有且只有一个父节点&a…...

linux vscode 下开发

linux vscode 下开发 javajdk插件查看调用层次 java jdk 各种JAVA JDK的镜像分发 编程宝库 - 技术改变世界 jdk 镜像 ubuntu22.04 安装 # Linux x64 64位 jdk-8u351-linux-x64.tar.gztar -zxf jdk-8u351-linux-x64.tar.gz mv jdk1.8.0_351 jdk8/ vim ~/.pr…...

【工具】python代码编辑器--PyCharm下载安装和介绍

PyCharm是一种Python IDE(集成开发环境),由JetBrains打造。它带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测试、版本控制等。此外,PyCharm还提供了一些高级功能,以用于支持Django框…...

SpringBoot第44讲:SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPX+Lua)

SpringBoot第44讲&#xff1a;SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPXLua) Redis实际使用场景最为常用的还有通过Redis实现分布式锁。本文是SpringBoot第44讲&#xff0c;主要介绍Redis实现分布式锁 文章目录 SpringBoot第44讲&#xff1a;SpringBoot集成Re…...

STM32F4X USART串口使用

STM32F4X USART串口使用 串口概念起始位波特率数据位停止位校验位串口间接线 STM32F4串口使用步骤GPIO引脚复用函数串口初始化函数串口例程 串口概念 串口是MCU与外部通信的重要通信接口&#xff0c;也是MCU在开发过程中的调试利器。串口通信有几个重要的参数&#xff0c;分别…...

python实现两个字符串比对差异点

一:代码实现 import difflib, re# 比较两个文本差异点 def compare_text_index(text1, text2):# 创建SequenceMatcher对象matcher = difflib.SequenceMatcher(a=text1, b=text2)# 获取差异报告diff_report = matcher.get_opcodes()# 检查差异报告中是否存在关键词错误for tag…...

SQLite数据库实现数据增删改查

当前文章介绍的设计的主要功能是利用 SQLite 数据库实现宠物投喂器上传数据的存储&#xff0c;并且支持数据的增删改查操作。其中&#xff0c;宠物投喂器上传的数据包括投喂间隔时间、水温、剩余重量等参数。 实现功能&#xff1a; 创建 SQLite 数据库表&#xff0c;用于存储宠…...

【Golang系统开发】搜索引擎(2) 压缩词典

写在前面 这篇文章我们就给出一系列的数据结构&#xff0c;使得词典能达到越来越高的压缩比。当然&#xff0c;和倒排索引记录表的大小相比&#xff0c;词典只占据了非常小的空间。那么为什么要对词典进行压缩呢&#xff1f; 这是因为决定信息检索系统的查询响应时间的一个重…...

clickhouse修改默认密码

1.明文密码 vim /etc/clickhouse-server/users.xml找到下面的语句,增加明文密码 <password>123456789</password> 2. sha256密码 # echo -n 123456789 | openssl dgst -sha256 (stdin) 15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 修改…...

基于java在线捐赠系统设计与实现

摘要 近年来&#xff0c;随着网络的快速发展&#xff0c;由于网络的开放性和便利性&#xff0c;具有广阔的发展前景。 本文设计并实现了医药捐赠系统。通过分析确定由两个不同的用户组成&#xff0c;每个用户具有不同的功能。它还可以帮助用户在线求助、申请项目、发表留言等&a…...

【前端】vscode javascript 代码片段失效问题解决

1. 文件--首选项--用户代码片段-vue.json : 添加 // { // // Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and // // description. Add comma separated ids of the languages where the snippet is app…...

AE-卡通人物解说动画视频的制作

目录 1.导入卡通人物图片和音频文件 2.新建合成 3.在卡通人物图片上添加效果和表达式 4.在音频文件上添加效果和表达式 5.将卡通人物中的 CC Split2 中分割1 表达式链接到滑块中 6.卡通人物根据音频文件自动匹配口型。 AE制作卡通人物解说视频&#xff0c;卡通人物口型根据…...

Linux 查看日志

在 Linux 中&#xff0c;内核日志使用 printk 函数进行输出。你可以通过以下方法查看 printk 的日志&#xff1a; 使用 dmesg 命令&#xff1a; dmesg 这个命令会显示内核环缓冲区中的日志消息&#xff0c;包括使用 printk 输出的消息。你可以通过滚动浏览输出来查看完整的日志…...

使用IO多路复用select完成TCP循环服务器接收客户端消息并打印

服务器 客户端 结果...

unity之Input.GetKeyDown与Input.GetKey区别

文章目录 Input.GetKeyDown与Input.GetKey区别 Input.GetKeyDown与Input.GetKey区别 Input.GetKey 和 Input.GetKeyDown 是 Unity 中用于检测按键状态的两个不同函数。它们之间的区别在于何时触发。 Input.GetKey(KeyCode key): 这个函数会在用户按住指定的键时触发&#xff0…...

excel 核心快捷键用法

1、wps怎样只复制公示计算出来的数据 1.1、按下快捷键“CtrlC”&#xff0c;复制该单元格。 1.2、按下快捷键“ShiftCtrlV”&#xff0c;即“粘贴为数值”&#xff0c;即可只复制数字而不复制该单元格的公式 1.3、wps怎样只复制公示计算出来的数据_百度知道https://zhidao.baid…...

postgresql

源码安装 ./configure --prefix/apps/pgsql make world -j4 make install-world useradd -s /bin/bash -m -d /home/postgres postgres echo -e ‘123456\n123456’ | passwd postgres mkdir -pv /pgsql/data chown postgres:postgres /pgsql/data/ 设置环境变量 vim /etc/…...

AutoSAR配置与实践(基础篇)3.2 BSW中的I/O架构和模块详解

传送门 -> AUTOSAR配置与实践总目录 AutoSAR配置与实践(基础篇)3.2 BSW中的I/O架构和模块详解 一、 BSW中的I/O架构和模块详解1.1 I/O 模块构成1.2 各子模块功能详解二、举例说明I/O 模块如何配合完成信号采集2.1 硬件处理先行 (step1-4)2.2 AUTOSAR软件登场(step 5-7)2.3…...

基于Java+SpringBoot+Vue的学校田径运动会管理系统【源码+论文+演示视频+包运行成功】

博主介绍&#xff1a;✌擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案…...

使用 Visual Studio Code Docker 工具调试 .NET 容器

作者&#xff1a;Chet Husk 排版&#xff1a;Alan Wang Visual Studio Code Docker 工具已发布1.26.0版本&#xff0c;这个版本为使用 .NET SDK 构建和调试容器映像提供了内置支持。 VS Code 中的 Docker 调试 Visual Studio Code Docker 工具使开发人员可以轻松入门容器。它…...

进程地址空间(比特课总结)

一、进程地址空间 1. 环境变量 1 &#xff09;⽤户级环境变量与系统级环境变量 全局属性&#xff1a;环境变量具有全局属性&#xff0c;会被⼦进程继承。例如当bash启动⼦进程时&#xff0c;环 境变量会⾃动传递给⼦进程。 本地变量限制&#xff1a;本地变量只在当前进程(ba…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

Spring数据访问模块设计

前面我们已经完成了IoC和web模块的设计&#xff0c;聪明的码友立马就知道了&#xff0c;该到数据访问模块了&#xff0c;要不就这俩玩个6啊&#xff0c;查库势在必行&#xff0c;至此&#xff0c;它来了。 一、核心设计理念 1、痛点在哪 应用离不开数据&#xff08;数据库、No…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

倒装芯片凸点成型工艺

UBM&#xff08;Under Bump Metallization&#xff09;与Bump&#xff08;焊球&#xff09;形成工艺流程。我们可以将整张流程图分为三大阶段来理解&#xff1a; &#x1f527; 一、UBM&#xff08;Under Bump Metallization&#xff09;工艺流程&#xff08;黄色区域&#xff…...

渗透实战PortSwigger Labs指南:自定义标签XSS和SVG XSS利用

阻止除自定义标签之外的所有标签 先输入一些标签测试&#xff0c;说是全部标签都被禁了 除了自定义的 自定义<my-tag onmouseoveralert(xss)> <my-tag idx onfocusalert(document.cookie) tabindex1> onfocus 当元素获得焦点时&#xff08;如通过点击或键盘导航&…...

LangChain【6】之输出解析器:结构化LLM响应的关键工具

文章目录 一 LangChain输出解析器概述1.1 什么是输出解析器&#xff1f;1.2 主要功能与工作原理1.3 常用解析器类型 二 主要输出解析器类型2.1 Pydantic/Json输出解析器2.2 结构化输出解析器2.3 列表解析器2.4 日期解析器2.5 Json输出解析器2.6 xml输出解析器 三 高级使用技巧3…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上

一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema&#xff0c;不需要复杂的查询&#xff0c;只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 &#xff1a;在几秒钟…...

Python第七周作业

Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt&#xff0c;并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径&#xff0c;并创建logs目录&#xff08;若不存在&#xff09; 3.递归遍历目录data&#xff0c;输出所有.csv文件的路径…...

【QT控件】显示类控件

目录 一、Label 二、LCD Number 三、ProgressBar 四、Calendar Widget QT专栏&#xff1a;QT_uyeonashi的博客-CSDN博客 一、Label QLabel 可以用来显示文本和图片. 核心属性如下 代码示例: 显示不同格式的文本 1) 在界面上创建三个 QLabel 尺寸放大一些. objectName 分别…...