Java处理子父级菜单的方式二
处理存在子父级关系的数据是写代码的过程中常见的操作,前面讲解过使用递归的方法来做,
可以参考这篇博客 https://www.cnblogs.com/yilangcode/p/16831867.html
今天来聊聊一种新的处理方式。使用List集合多轮遍历,添加子父级菜单信息。
建表SQL
DROP TABLE IF EXISTS `sa_menu`;
CREATE TABLE `sa_menu` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`menu_code` varchar(16) DEFAULT NULL COMMENT '菜单编号',
`menu_parent_code` varchar(32) DEFAULT NULL COMMENT '菜单上级编号',
`menu_name` varchar(100) DEFAULT NULL COMMENT '菜单名称',
`menu_marks` varchar(100) DEFAULT NULL COMMENT '备注',
`menu_url` varchar(100) DEFAULT NULL COMMENT '菜单路径',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;
数据
INSERT INTO `sa_menu` VALUES ('1', '1', '0', '一级菜单一', '一级菜单一', 'one_level_menu');
INSERT INTO `sa_menu` VALUES ('2', '2', '1', '子菜-1-1', '子菜-1-1', 'child_menu_one');
INSERT INTO `sa_menu` VALUES ('3', '3', '1', '子菜-1-2', '子菜-1-2', 'child_menu_two');
INSERT INTO `sa_menu` VALUES ('4', '4', '0', '一级菜单二', '一级菜单二', 'two_level_menu');
INSERT INTO `sa_menu` VALUES ('5', '5', '4', '子菜单-2-1', '子菜单-2-1', 'child_menu_three');
INSERT INTO `sa_menu` VALUES ('6', '6', '4', '子菜单-2-2', '子菜单-2-2', 'child_menu_four');
INSERT INTO `sa_menu` VALUES ('7', '7', '4', '子菜单-2-3', '子菜单-2-3', 'child_menu_five');
数据如下

它们的层级关系如下所示

问题是如何构建这样带层级关系的子父级结构的数据呢?经过在实际项目中的
检验,测试,可以使用一种比较简单的方式来处理,下面逐一说明。
构建的菜单类如下
@Data
public class SaMenuPo {
/*
* 主键ID
* */
private int id;
/*
* 菜单 code
* */
private String menuCode;
/*
* 菜单父 code
* */
private String menuParentCode;
/*
* 菜单名称
* */
private String menuName;
/*
* 备注
* */
private String menuRemake;
/*
* 菜单 url
* */
private String menuUrl;
/*
* 子菜单
* */
private List<SaMenuPo> childMenu;
}
大致的实现思路如下:
查询数据时,将菜单数据全部取出,按照升序排序,便于处理的菜单数据是按照顺序排列。
遍历集合的时候,每次添加一条菜单数据,则删除该条菜单数据,可以提高效率,到最后的时候
传入的菜单都会被删除。删除后的菜单信息保存到一个新的菜单列表当中,在这个新的菜单列表中
来构建具有子父级关系的菜单信息。需要使用到集合遍历时的迭代器,来进行删除最简单。或者查询时
降序排序,添加时倒着遍历集合,然后添加元素后直接删除即可。
查询方式如下
<select id="menuTest" resultType="mybatis.po.SaMenuPo">
SELECT
t.id,
t.menu_code menuCode,
t.menu_parent_code menuParentCode,
t.menu_name menuName,
t.menu_marks menuMarks,
t.menu_url menuUrl
FROM sa_menu t
order by t.menu_code asc
</select>
主要的处理代码如下
private List<SaMenuPo> handlerMenu(List<SaMenuPo> menuPoList){
List<SaMenuPo> resultList = new ArrayList<>();
// 首先获取一级菜单,添加一个菜单之后,就删除一个菜单
Iterator<SaMenuPo> iterator = menuPoList.iterator();
SaMenuPo tempMenu;
while (iterator.hasNext()){
tempMenu = iterator.next();
// 可以确定的一点是,一级菜单的 menuParentCode 为0
if("0".equals(tempMenu.getMenuParentCode())){
// 添加一级菜单
resultList.add(tempMenu);
// 删除菜单
iterator.remove();
}
}
// 添加二级菜单
for(int i = 0; i < resultList.size(); i++){
List<SaMenuPo> childMenu = new ArrayList<>();
// 需要重新获取迭代器,因为添加完一次菜单后,原菜单列表menuPoList中总数有变化
iterator = menuPoList.iterator();
while (iterator.hasNext()){
tempMenu = iterator.next();
// 一级菜单的menuCode为二级菜单的 menuParentCode
if(resultList.get(i).getMenuCode().equals(tempMenu.getMenuParentCode())){
// 添加二级菜单
childMenu.add(tempMenu);
// 删除二级菜单
iterator.remove();
}
}
// 添加完一个一级菜单的所有子菜单,则设置子菜单
resultList.get(i).setChildMenu(childMenu);
}
return resultList;
}
返回的JSON数据格式如下
{"responseDate":"Sat Jul 15 11:18:48 CST 2023","status":"SUCCESS","errorCode":"","errorMessage":"","responseBody":[{"id":1,"menuCode":"1","menuParentCode":"0","menuName":"一级菜单一","menuMarks":"一级菜单一","menuUrl":"one_level_menu","childMenu":[{"id":2,"menuCode":"2","menuParentCode":"1","menuName":"子菜单-1-1","menuMarks":"子菜单-1-1","menuUrl":"child_menu_one","childMenu":null},{"id":3,"menuCode":"3","menuParentCode":"1","menuName":"子菜单-1-2","menuMarks":"子菜单-1-2","menuUrl":"child_menu_two","childMenu":null}]},{"id":4,"menuCode":"4","menuParentCode":"0","menuName":"一级菜单二","menuMarks":"一级菜单二","menuUrl":"two_level_menu","childMenu":[{"id":5,"menuCode":"5","menuParentCode":"4","menuName":"子菜单-2-1","menuMarks":"子菜单-2-1","menuUrl":"child_menu_three","childMenu":null},{"id":6,"menuCode":"6","menuParentCode":"4","menuName":"子菜单-2-2","menuMarks":"子菜单-2-2","menuUrl":"child_menu_four","childMenu":null},{"id":7,"menuCode":"7","menuParentCode":"4","menuName":"子菜单-2-3","menuMarks":"子菜单-2-3","menuUrl":"child_menu_five","childMenu":null}]}]}
然后去这个网站 https://jsoncrack.com/editor 将返回的数据放在左侧部分,整个JSON数据的结构就会
很清晰的显示出来。页面中拿到数据后再去渲染菜单就会方便很多。

这里只有两层菜单,处理起来比较方便,只需要处理两次即可。如果有三层呢?那就需要在处理一次,先遍历一级菜单,
然后遍历二级菜单,添加二级菜单的子菜单数据。这时候就可以考虑使用递归来做,代码会更加地简洁,使用更加地方便。
上面的菜单使用递归方式来处理如下,需要拆分为如下的两个方法来处理,这样就可以处理三级,四级菜单;或者是处理子父级
关系的数据比如省-市-区-县数据;或者是其他有子父级关系的数据;或是具有层级关系的数据,比如文件夹、文件数据。
private List<SaMenuPo> handlerOneLevelMenu(List<SaMenuPo> menuPoList){
List<SaMenuPo> resultMenu = new ArrayList<>();
for(SaMenuPo saMenuPo : menuPoList){
if("0".equals(saMenuPo.getMenuParentCode())){
// 添加一级菜单
resultMenu.add(saMenuPo);
}
}
for(SaMenuPo saMenuPo : resultMenu){
recursionHandlerMenu(menuPoList, saMenuPo);
}
return resultMenu;
}
private SaMenuPo recursionHandlerMenu(List<SaMenuPo> menuPoList, SaMenuPo parentSaMenuPo){
List<SaMenuPo> childMenu = new ArrayList<>();
for(SaMenuPo saMenuPo : menuPoList){
if(parentSaMenuPo.getMenuCode().equals(saMenuPo.getMenuParentCode())){
// 有子菜单数据,则递归调用
recursionHandlerMenu(menuPoList, saMenuPo);
// 添加子菜单数据
childMenu.add(saMenuPo);
}
}
if(childMenu.size() < 1){
// 递归调用时,没有子菜单数据,则直接返回
return parentSaMenuPo;
}
// 有子菜单数据,则添加子菜单数据
parentSaMenuPo.setChildMenu(childMenu);
return parentSaMenuPo;
}
为了演示方便,这时候添加一条三级菜单数据,如下
INSERT INTO `sa_menu` (`id`, `menu_code`, `menu_parent_code`, `menu_name`, `menu_marks`, `menu_url`) VALUES ('8', '8', '2', '子菜单-3-1', '子菜单-3-1', 'child_menu_six');
然后进行测试,返回数据如下
{"responseDate":"Sat Jul 15 11:55:49 CST 2023","status":"SUCCESS","errorCode":"","errorMessage":"","responseBody":[{"id":1,"menuCode":"1","menuParentCode":"0","menuName":"一级菜单一","menuMarks":"一级菜单一","menuUrl":"one_level_menu","childMenu":[{"id":2,"menuCode":"2","menuParentCode":"1","menuName":"子菜单-1-1","menuMarks":"子菜单-1-1","menuUrl":"child_menu_one","childMenu":[{"id":8,"menuCode":"8","menuParentCode":"2","menuName":"子菜单-3-1","menuMarks":"子菜单-3-1","menuUrl":"child_menu_six","childMenu":null}]},{"id":3,"menuCode":"3","menuParentCode":"1","menuName":"子菜单-1-2","menuMarks":"子菜单-1-2","menuUrl":"child_menu_two","childMenu":null}]},{"id":4,"menuCode":"4","menuParentCode":"0","menuName":"一级菜单二","menuMarks":"一级菜单二","menuUrl":"two_level_menu","childMenu":[{"id":5,"menuCode":"5","menuParentCode":"4","menuName":"子菜单-2-1","menuMarks":"子菜单-2-1","menuUrl":"child_menu_three","childMenu":null},{"id":6,"menuCode":"6","menuParentCode":"4","menuName":"子菜单-2-2","menuMarks":"子菜单-2-2","menuUrl":"child_menu_four","childMenu":null},{"id":7,"menuCode":"7","menuParentCode":"4","menuName":"子菜单-2-3","menuMarks":"子菜单-2-3","menuUrl":"child_menu_five","childMenu":null}]}]}
最终展示的JSON数据结构如下

这篇博客就写到这里,如果有其他更好建议的小伙伴,欢迎留言讨论。
Java处理子父级菜单的方式二的更多相关文章
- element中的树形组件,如何获取父级菜单的id
一般多选的树形组件,使用getCheckedNodes()方法只能获取到本级的菜单id,只有在子菜单全部选中的情况下才会选中上级.但我们想要不全选中子级的情况下也要获取它的上级,甚至上上级等,怎么办呢 ...
- element ui el-tree回显问题及提交问题(当后台返回的el-tree相关数组的时候,子菜单未全部选中,但是只要父级菜单的id在数组中,那么他的子菜单为全部选中状态)
1:问题原因:我们可能使用 this.$refs.tree.setCheckedKeys(this.defalutArr);或者使用:default-expanded-keys="treeD ...
- 使用Oracle数据库实现树形结构表的子-父级递归查询和删除,通过级联菜单简单举例
前言: 我们在开发中,常常遇到单表的子-父id级联的表结构,在树形的深度不确定的情况下,一次查询出某个树形结构下的所有具有子-父级关系的数据变得十分困难. 这时,我们使用oracle提供的CONNEC ...
- 使用Oracle数据库实现树形结构表的子-父级迭代(递归)查询和删除,通过级联菜单简单举例
前言: 我们在开发中,常常遇到单表的子-父id级联的表结构,在树形的深度不确定的情况下,一次查询出某个树形结构下的所有具有子-父级关系的数据变得十分困难. 这时,我们使用oracle提供的CONNEC ...
- vue 如何通过监听路由变化给父级路由菜单添加active样式
1.项目需求:在项目开发中,多级菜单的情况下,勾选子菜单时,需要在父级菜单添加active样式. 2.遇到的问题:一级路由菜单的话,点击当前路由会自动在路由标签上添加router-link-exact ...
- 使用postgre数据库实现树形结构表的子-父级迭代查询,通过级联菜单简单举例
前言:开发常用的关系型数据库MySQL,mssql,postgre,Oracle,简单的增删改查的SQL语句都与标准SQL兼容,这个不用讲,那么对于迭代查询(不严格的叫法:递归查询)每种数据库都不一样 ...
- (转)asp.net(C#)手记之Repeater与两级菜单
先来张图片说明下我们要实现的菜单: 这个菜单只实现了2级哈. 我采用的方法是嵌套2个Repeater. 先看下数据库中的表结构: 数据: 上代码: aspx: <asp:Repeater ID= ...
- Axure案例:用中继器实现便捷好用的3级菜单--转载
提示1:本篇教程可能不太适合新手,以及不了解中继器.全局变量.系统变量等使用的…新手 提示2:文字其实不多,截图太多,所以看上去很长,也可直接翻到末尾查看所有的用例,其实并不多 之前有介绍过使用中继器 ...
- java List递归排序,传统方式和java8 Stream优化递归,无序的列表按照父级关系进行排序(两种排序类型)
当有一个List列表是无序的,List中的数据有parentid进行关联,通过java排序成两种排序类型: 所用的测试列表最顶级无parentid,若为特殊值,修改下判断方法即可. 第一种排序:按照树 ...
- Java IO,io,文件操作,删除文件,删除文件夹,获取文件父级目录
Java IO,io,文件操作,删除文件,删除文件夹,获取文件父级目录 这里先简单的贴下常用的方法: File.separator //当前系统文件分隔符 File.pathSeparator // ...
随机推荐
- SqlSugar多库/多租户
1. 多库和多租户 如果是接口ISugarClient先看一下标题6,看完在看这儿 1.1 固定多数据库模式 数据库数量是固定的一般在声明的全部加上 //通过ConfigId进行数据库区分 var d ...
- 基于知识图谱的电影知识问答系统:训练TF-IDF 向量算法和朴素贝叶斯分类器、在 Neo4j 中查询
基于知识图谱的电影知识问答系统:训练TF-IDF 向量算法和朴素贝叶斯分类器.在 Neo4j 中查询 1.项目介绍 训练 TF-IDF 向量算法和朴素贝叶斯分类器,预测用户文本所属的问题类别 使用分词 ...
- 强化学习从基础到进阶-案例与实践[3]:表格型方法:Sarsa、Qlearning;蒙特卡洛策略、时序差分等以及Qlearning项目实战
强化学习从基础到进阶-案例与实践[3]:表格型方法:Sarsa.Qlearning:蒙特卡洛策略.时序差分等以及Qlearning项目实战 策略最简单的表示是查找表(look-up table),即表 ...
- MySQL【二】---数据库查询详细教程{查询、排序、聚合函数、分组}
1.数据准备.基本的查询(回顾一下) 创建数据库 create database python_test charset=utf8; 查看数据库: show databases; 使用数据库: use ...
- 【7】python_matplotlib 输出(保存)矢量图方法;画图时图例说明(legend)放到图像外侧;Python_matplotlib图例放在外侧保存时显示不完整问题解决
1.python_matplotlib 输出(保存)矢量图方法 用python的matplotlib画出的图,一般是需要保存到本地使用的.如果是用show()展出的图,再右键保存,这样的图是失帧而非矢 ...
- python自动化高效办公第二期,带你项目实战【二】{数据可视化、发送邮件(定时任务监控)、python聊天机器人(基于微信、钉钉)}
相关文章和数据源: python自动化高效办公第二期,带你项目实战[一]{excel数据处理.批量化生成word模板.pdf和ppt等自动化操作} Python自动化办公--Pandas玩转Excel ...
- 6.2 Windows驱动开发:内核枚举SSSDT表基址
在Windows内核中,SSSDT(System Service Shadow Descriptor Table)是SSDT(System Service Descriptor Table)的一种变种 ...
- MASA学习和总结
一.MASA概述 MASA是温州数闪科技推出的开源产品,目前有三个产品线,分别是MASA Stack,MASA Framework,MASA Blazor. MASA Stack:是一个开源.企业级. ...
- (python)代码学习||2024.2.3||题目是codewars上的【Validate Sudoku with size `NxN`】
题目的要求是写一个Sudoku类,类中要有一个实例函数判断传给对象的二维数组是否符合数独规则 题目链接:https://www.codewars.com/kata/540afbe2dc9f615d5e ...
- webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中
webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMultiConnection的demo中 目录 webrtc终极版(二)搭建自己的iceserver服务,并用到RTCMulti ...