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. 树形结构 树形结构,是指:数据元素之间的关系像一颗树的数据结构。由树根延伸出多个树杈 它具有以下特点: 每个节点都只有有限个子节点或无子节点;没有父节点的节点称为根节点;每一个非根节点有且只有一个父节点&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讲:SpringBoot集成Redis - Redis分布式锁的实现之Jedis(setNXPXLua) Redis实际使用场景最为常用的还有通过Redis实现分布式锁。本文是SpringBoot第44讲,主要介绍Redis实现分布式锁 文章目录 SpringBoot第44讲:SpringBoot集成Re…...
STM32F4X USART串口使用
STM32F4X USART串口使用 串口概念起始位波特率数据位停止位校验位串口间接线 STM32F4串口使用步骤GPIO引脚复用函数串口初始化函数串口例程 串口概念 串口是MCU与外部通信的重要通信接口,也是MCU在开发过程中的调试利器。串口通信有几个重要的参数,分别…...
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 数据库实现宠物投喂器上传数据的存储,并且支持数据的增删改查操作。其中,宠物投喂器上传的数据包括投喂间隔时间、水温、剩余重量等参数。 实现功能: 创建 SQLite 数据库表,用于存储宠…...
【Golang系统开发】搜索引擎(2) 压缩词典
写在前面 这篇文章我们就给出一系列的数据结构,使得词典能达到越来越高的压缩比。当然,和倒排索引记录表的大小相比,词典只占据了非常小的空间。那么为什么要对词典进行压缩呢? 这是因为决定信息检索系统的查询响应时间的一个重…...
clickhouse修改默认密码
1.明文密码 vim /etc/clickhouse-server/users.xml找到下面的语句,增加明文密码 <password>123456789</password> 2. sha256密码 # echo -n 123456789 | openssl dgst -sha256 (stdin) 15e2b0d3c33891ebb0f1ef609ec419420c20e320ce94c65fbc8c3312448eb225 修改…...
基于java在线捐赠系统设计与实现
摘要 近年来,随着网络的快速发展,由于网络的开放性和便利性,具有广阔的发展前景。 本文设计并实现了医药捐赠系统。通过分析确定由两个不同的用户组成,每个用户具有不同的功能。它还可以帮助用户在线求助、申请项目、发表留言等&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制作卡通人物解说视频,卡通人物口型根据…...
Linux 查看日志
在 Linux 中,内核日志使用 printk 函数进行输出。你可以通过以下方法查看 printk 的日志: 使用 dmesg 命令: dmesg 这个命令会显示内核环缓冲区中的日志消息,包括使用 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): 这个函数会在用户按住指定的键时触发࿰…...
excel 核心快捷键用法
1、wps怎样只复制公示计算出来的数据 1.1、按下快捷键“CtrlC”,复制该单元格。 1.2、按下快捷键“ShiftCtrlV”,即“粘贴为数值”,即可只复制数字而不复制该单元格的公式 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的学校田径运动会管理系统【源码+论文+演示视频+包运行成功】
博主介绍:✌擅长Java、微信小程序、Python、Android等,专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟 Java项目精品实战案…...
使用 Visual Studio Code Docker 工具调试 .NET 容器
作者:Chet Husk 排版:Alan Wang Visual Studio Code Docker 工具已发布1.26.0版本,这个版本为使用 .NET SDK 构建和调试容器映像提供了内置支持。 VS Code 中的 Docker 调试 Visual Studio Code Docker 工具使开发人员可以轻松入门容器。它…...
AI引擎助力,CamScanner智能高清滤镜开启扫描新纪元!
文章目录 ⭐ 写在前面⭐ 突破图像处理难点:扫描全能王的独特优势⭐ 耳听为虚,眼见为实⭐ 产品背后的主要核心:AI-Scan助力⭐ 深度学习助力智能文档处理的国际化进程⭐ 品味智能文档处理的轻松与精准 ⭐ 写在前面 在数字化快速发展的今天&…...
opencv进阶07-支持向量机cv2.ml.SVM_create()简介及示例
支持向量机(Support Vector Machine,SVM)是一种二分类模型,目标是寻找一个标准(称为超平面)对样本数据进行分割,分割的原则是确保分类最优化(类别之间的间隔最大)。当数据…...
LA@n维向量@解析几何向量和线性代数向量
文章目录 概念n维向量向量类型实向量和复向量行向量和列向量行列向量的转换特殊向量向量运算 矩阵的向量分块👺 解析几何向量和线性代数向量👺向量空间 n n n维向量空间 n n n维空间的 n − 1 n-1 n−1维超平面 概念 n维向量 由 n n n个有次序的数 a …...
go 协程并发数控制
错误的写法: 这里的<-ch 是为了从channel 中读取 数据,为了不使channel通道被写满,阻塞 go 协程数的创建。但是请注意,go workForDraw(v, &wg) 是不阻塞后续的<-ch 执行的,所以就一直go workForDraw(v, &…...
MySQL的安装以及卸载
下载官网 https://www.mysql.com/ 切到下载tab页 找到 MySQL Community Server 或者 MySQL Community (GPL) Downloads --> MySQL Community Server 点击download按钮: 点击download进入下载页面选择No thanks, just start my download就可以开始下载了。 下…...
LRU算法与Caffeine、Redis中的缓存淘汰策略
推荐阅读 AI文本 OCR识别最佳实践 AI Gamma一键生成PPT工具直达链接 玩转cloud Studio 在线编码神器 玩转 GPU AI绘画、AI讲话、翻译,GPU点亮AI想象空间 资源分享 「java、python面试题」来自UC网盘app分享,打开手机app,额外获得1T空间 https://dr…...
HTML笔记(3)
表单标签 用于登录、注册界面,以采集用户输入的信息,把信息采集到之后,用户一点按钮,就会把这些信息发送到服务端,服务端就可以把这些数据存储到数据库,所以表单是一个非常重要的html标签,它主要…...
c++——重写(覆盖),实际上对应的就是虚函数
重写是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同(花括号内),派生类调用时会调用派生类的重写函数,不会调用被重写函数…...
算法通关村——字符串反转问题解析
1. 反转字符串 反转字符串 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。 1.1 交换 这一题的思路还是简单的&…...
vue + elementui 中 在弹框中使用了 tree型结构(<el-tree></el-tree>),点击关闭按钮按钮重置tree
vue 项目中使用了element-ui 中 tree,选择了懒加载的模式 通过点击按钮,使得 tree 重新加载 <div class"head-container header-tree" v-if"addDialogVisible"><el-treeref"tree":data"treeData":loa…...
公司做网站怎么样/网站seo批量查询工具
智慧 毅力 无所不能 正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性... Lua和C交互详细总结 转自:http://cn.cocos2d-x.org/tutorial/show?id1474 一、Lua堆栈 要理解Lua和C交互,首先要理解Lua堆栈。 简单来说࿰…...
重庆网站开发商城/今天国际新闻最新消息
一、python语言基础 1.1变量变量.png 1.2数据类型数据类型.png 1.3序列 序列分类:可变序列list,不可变序列tuple、str。在python中,内建了6中序列:列表、元组、字符串、unicode字符串、buffer对象、xrange对象。 (1)list列表list列…...
做一款小说网站/谷歌搜索指数查询
正在做一个消费管理系统(就是自己想功能,自己不停的写东西)。接下来写的都是在做的时候遇到的一些问题,有的很快解决了,有的花了点时间。 (1)、如何在JFrame中里面插入一张背景图片呢ÿ…...
网站建设公司做销售前景好不好?/广州信息流推广公司
前言 大家好,今天给大家讲一讲Python的综合实战演练内容——以大众都非常喜爱的火锅为例,如何用我们掌握的Python语言,去获取全国不同城市火锅店数量情况,并将这些数据进行可视化展示呢? 接下来我将带大家实现这一操…...
一般做外单的有哪些网站/315影视行业
1. 卷积神经网络结构 卷积神经网络是一个多层的神经网络,每层都是一个变换(映射),常用卷积convention变换和pooling池化变换,每种变换都是对输入数据的一种处理,是输入特征的另一种特征表达;每层…...
织梦wordpress帝国对比/市场调研流程
大浪淘沙的AI创业圈,触景无限正在这个技术浪潮中不断找寻自己的位置。 有人说,AI创业的大门已经慢慢关闭,场内的玩家也已基本定型,未来的战争将聚焦在头部几家,马太效应的收紧会使得一大波竞逐者生存艰难,…...