iis服务器助手广告广告
返回顶部
首页 > 资讯 > 数据库 >树形结构的菜单表设计与查询
  • 377
分享到

树形结构的菜单表设计与查询

树形结构的菜单表设计与查询 2016-09-24 21:09:39 377人浏览 才女
摘要

开发中经常会遇到树形结构的场景,比如:导航菜单、组织机构等等,但凡是有这种父子层级结构的都是如此,一级类目、二级类目、三级类目。。。 对于这种树形结构的表要如何设计呢?接下来一起探讨一下 首先,想一个问题,用非关系型数据库存储可不可以?

树形结构的菜单表设计与查询

开发中经常会遇到树形结构的场景,比如:导航菜单、组织机构等等,但凡是有这种父子层级结构的都是如此,一级类目、二级类目、三级类目。。。

对于这种树形结构的表要如何设计呢?接下来一起探讨一下

首先,想一个问题,用非关系型数据库存储可不可以?

答案是肯定可以的,比如用mongoDB,直接将整棵树存成JSON。但是,这样不利于按条件查询,当然也取决于具体的需求,抛开需求谈设计都是耍流氓。

在菜单这个场景下,一般还是用关系型数据库存储,可以将最终的查询结构缓存起来。

常用的方法有四种:

  • 每一条记录存parent_id
  • 每一条记录存整个tree path经过的node枚举
  • 每一条记录存 nleft 和 nright
  • 维护一个表,所有的tree path作为记录进行保存

 

第一种:每条记录存储parent_id

这种方式简单明了,但是想要查询某个节点的所有父级和子级的时候比较困难,势必需要用到递归,在Mysql里面就得写存储过程,太麻烦了。

当然,如果只有两级的话就比较简单了,自连接就搞定了,例如:

第四种:单独用一种表保存节点之间的关系

CREATE TABLE `city`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(16),
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4;

CREATE TABLE `city_tree_path_info`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `city_id` int(11) NOT NULL,
  `ancestor_id` int(11) NOT NULL COMMENT "祖先ID",
  `level` tinyint(4) NOT NULL COMMENT "层级",
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4;

上面这个例子中,city表代表城市,city_tree_path_info代表城市之间的层级关系,ancestor_id表示父级和祖父级ID,level是当前记录相对于ancestor_id而言的层级。这样就把整个层级关系保存到这张表中了,以后想查询某个节点的所有父级和子级就很容易了。

最后,我发现构造这种层级树最简单的还是用java代码

java递归生成菜单树

Menu.java 

 1 package com.example.demo.model;
 2 
 3 import lombok.AllArgsConstructor;
 4 import lombok.Data;
 5 import lombok.NoArgsConstructor;
 6 
 7 import java.util.List;
 8 
 9 @AllArgsConstructor
10 @NoArgsConstructor
11 @Data
12 public class Menu {
13 
14     
17     private Integer id;
18 
19     
22     private Integer pid;
23 
24     
27     private String name;
28 
29     
32     private String code;
33 
34     
37     private String url;
38 
39     
42     private String icon;
43 
44     
47     private int sort;
48 
49     
52     private List<Menu> children;
53 
54     public Menu(Integer id, Integer pid, String name, String code, String url, String icon, int sort) {
55         this.id = id;
56         this.pid = pid;
57         this.name = name;
58         this.code = code;
59         this.url = url;
60         this.icon = icon;
61         this.sort = sort;
62     }
63 
64 }

Test.java

 1 package com.example.demo.model;
 2 
 3 import com.fasterxml.jackson.core.jsonProcessingException;
 4 import com.fasterxml.jackson.databind.ObjectMapper;
 5 
 6 import java.util.ArrayList;
 7 import java.util.Comparator;
 8 import java.util.List;
 9 import java.util.stream.Collectors;
10 
11 public class Hello {
12     public static void main(String[] args) throws JsonProcessingException {
13         List<Menu> allMenuList = new ArrayList<>();
14         allMenuList.add(new Menu(1, 0, "湖北", "HuBei", "/a", "a", 3));
15         allMenuList.add(new Menu(2, 0, "河南", "HeNan", "/b", "b", 2));
16         allMenuList.add(new Menu(3, 1, "宜昌", "YiChang", "/c", "c", 2));
17         allMenuList.add(new Menu(4, 2, "信阳", "XinYang", "/d", "d", 1));
18         allMenuList.add(new Menu(5, 1, "随州", "SuiZhou", "/e", "e", 1));
19         allMenuList.add(new Menu(6, 5, "随县", "SuiXian", "/f", "f", 2));
20         allMenuList.add(new Menu(7, 3, "枝江", "ZhiJiang", "/g", "g", 2));
21 
22         //  一级菜单
23         List<Menu> parentList = allMenuList.stream().filter(e->e.getPid()==0).sorted(Comparator.comparing(Menu::getSort)).collect(Collectors.toList());
24         //  递归调用,为所有一级菜单设置子菜单
25         for (Menu menu : parentList) {
26             menu.setChildren(getChild(menu.getId(), allMenuList));
27         }
28 
29         ObjectMapper objectMapper = new ObjectMapper();
30         System.out.println(objectMapper.writeValueAsString(parentList));
31     }
32 
33     
39     public static List<Menu> getChild(Integer id, List<Menu> allList) {
40         //  子菜单
41         List<Menu> childList = new ArrayList<>();
42         for (Menu menu : allList) {
43             if (menu.getPid().equals(id)) {
44                 childList.add(menu);
45             }
46         }
47 
48         //  为子菜单设置子菜单
49         for (Menu nav : childList) {
50             nav.setChildren(getChild(nav.getId(), allList));
51         }
52 
53         //  排序
54         childList = childList.stream().sorted(Comparator.comparing(Menu::getSort)).collect(Collectors.toList());
55 
56         if (childList.size() == 0) {
57 //            return null;
58             return new ArrayList<>();
59         }
60         return childList;
61     }
62 }

结果:

 1 [
 2     {
 3         "id":2,
 4         "pid":0,
 5         "name":"河南",
 6         "code":"HeNan",
 7         "url":"/b",
 8         "icon":"b",
 9         "sort":2,
10         "children":[
11             {
12                 "id":4,
13                 "pid":2,
14                 "name":"信阳",
15                 "code":"XinYang",
16                 "url":"/d",
17                 "icon":"d",
18                 "sort":1,
19                 "children":[]
20             }
21         ]
22     },
23     {
24         "id":1,
25         "pid":0,
26         "name":"湖北",
27         "code":"HuBei",
28         "url":"/a",
29         "icon":"a",
30         "sort":3,
31         "children":[
32             {
33                 "id":5,
34                 "pid":1,
35                 "name":"随州",
36                 "code":"SuiZhou",
37                 "url":"/e",
38                 "icon":"e",
39                 "sort":1,
40                 "children":[
41                     {
42                         "id":6,
43                         "pid":5,
44                         "name":"随县",
45                         "code":"SuiXian",
46                         "url":"/f",
47                         "icon":"f",
48                         "sort":2,
49                         "children":[]
50                     }
51                 ]
52             },
53             {
54                 "id":3,
55                 "pid":1,
56                 "name":"宜昌",
57                 "code":"YiChang",
58                 "url":"/c",
59                 "icon":"c",
60                 "sort":2,
61                 "children":[
62                     {
63                         "id":7,
64                         "pid":3,
65                         "name":"枝江",
66                         "code":"ZhiJiang",
67                         "url":"/g",
68                         "icon":"g",
69                         "sort":2,
70                         "children":[]
71                     }
72                 ]
73             }
74         ]
75     }
76 ]

参考:

https://www.cnblogs.com/w2206/p/10490208.html

Https://www.cnblogs.com/mokinGone/p/9109021.html

https://www.cnblogs.com/makai/p/12301707.html

https://www.cnblogs.com/zhifengge/p/6910881.html 

 

您可能感兴趣的文档:

--结束END--

本文标题: 树形结构的菜单表设计与查询

本文链接: https://www.lsjlt.com/news/8396.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • oracle怎么查询当前用户所有的表
    要查询当前用户拥有的所有表,可以使用以下 sql 命令:select * from user_tables; 如何查询当前用户拥有的所有表 要查询当前用户拥有的所有表,可以使...
    99+
    2024-05-15
    oracle
  • oracle怎么备份表中数据
    oracle 表数据备份的方法包括:导出数据 (exp):将表数据导出到外部文件。导入数据 (imp):将导出文件中的数据导入表中。用户管理的备份 (umr):允许用户控制备份和恢复过程...
    99+
    2024-05-15
    oracle
  • oracle怎么做到数据实时备份
    oracle 实时备份通过持续保持数据库和事务日志的副本来实现数据保护,提供快速恢复。实现机制主要包括归档重做日志和 asm 卷管理系统。它最小化数据丢失、加快恢复时间、消除手动备份任务...
    99+
    2024-05-15
    oracle 数据丢失
  • oracle怎么查询所有的表空间
    要查询 oracle 中的所有表空间,可以使用 sql 语句 "select tablespace_name from dba_tablespaces",其中 dba_tabl...
    99+
    2024-05-15
    oracle
  • oracle怎么创建新用户并赋予权限设置
    答案:要创建 oracle 新用户,请执行以下步骤:以具有 create user 权限的用户身份登录;在 sql*plus 窗口中输入 create user identified ...
    99+
    2024-05-15
    oracle
  • oracle怎么建立新用户
    在 oracle 数据库中创建用户的方法:使用 sql*plus 连接数据库;使用 create user 语法创建新用户;根据用户需要授予权限;注销并重新登录以使更改生效。 如何在 ...
    99+
    2024-05-15
    oracle
  • oracle怎么创建新用户并赋予权限密码
    本教程详细介绍了如何使用 oracle 创建一个新用户并授予其权限:创建新用户并设置密码。授予对特定表的读写权限。授予创建序列的权限。根据需要授予其他权限。 如何使用 Oracle 创...
    99+
    2024-05-15
    oracle
  • oracle怎么查询时间段内的数据记录表
    在 oracle 数据库中查询指定时间段内的数据记录表,可以使用 between 操作符,用于比较日期或时间的范围。语法:select * from table_name wh...
    99+
    2024-05-15
    oracle
  • oracle怎么查看表的分区
    问题:如何查看 oracle 表的分区?步骤:查询数据字典视图 all_tab_partitions,指定表名。结果显示分区名称、上边界值和下边界值。 如何查看 Oracle 表的分区...
    99+
    2024-05-15
    oracle
  • oracle怎么导入dump文件
    要导入 dump 文件,请先停止 oracle 服务,然后使用 impdp 命令。步骤包括:停止 oracle 数据库服务。导航到 oracle 数据泵工具目录。使用 impdp 命令导...
    99+
    2024-05-15
    oracle
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作