java 从数据库读取菜单,递归生成菜单树

本贴最后更新于 2914 天前,其中的信息可能已经渤澥桑田

首先看一下菜单的样子

根据这个样子我们定义菜单类

public class Menu {
    // 菜单id
    private String id;
    // 菜单名称
    private String name;
    // 父菜单id
    private String parentId;
    // 菜单url
    private String url;
    // 菜单图标
    private String icon;
    // 菜单顺序
    private int order;
    // 子菜单
    private List<Menu> childMenus;
    // ... 省去getter和setter方法以及toString方法
}

我们根据这个类定义数据库,并插入菜单数据

DROP TABLE IF EXISTS `jrbac_menu`;
CREATE TABLE `jrbac_menu` (
  `id` varchar(32) NOT NULL COMMENT '主键id,uuid32位',
  `name` varchar(64) NOT NULL COMMENT '登录用户名',
  `parent_id` varchar(32) DEFAULT NULL COMMENT '父菜单id',
  `url` varchar(64) DEFAULT NULL COMMENT '访问地址',
  `icon` varchar(32) DEFAULT NULL COMMENT '菜单图标',
  `order` tinyint(4) DEFAULT '0' COMMENT '菜单顺序',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='菜单表';

-- ----------------------------
-- Records of jrbac_menu
-- ----------------------------
INSERT INTO `jrbac_menu` VALUES ('1', 'Forms', null, 'forms.html', 'fa fa-edit', '0');
INSERT INTO `jrbac_menu` VALUES ('2', 'UI Elements', null, '', 'fa fa-wrench', '1');
INSERT INTO `jrbac_menu` VALUES ('3', 'Buttons', '2', 'buttons.html', '', '0');
INSERT INTO `jrbac_menu` VALUES ('4', 'Icons', '2', 'icons.html', null, '1');
INSERT INTO `jrbac_menu` VALUES ('5', 'Multi-Level Dropdown', '', '', 'fa fa-sitemap', '2');
INSERT INTO `jrbac_menu` VALUES ('6', 'Second Level Item', '5', 'second.html', null, '0');
INSERT INTO `jrbac_menu` VALUES ('7', 'Third Level', '5', null, '', '1');
INSERT INTO `jrbac_menu` VALUES ('8', 'Third Level Item', '7', 'third.html', null, '0');

为了演示,我们把可展开的没有做完,仅仅插入几条数据能出效果就可以了。

测试方法与递归方法

private final Gson gson = new GsonBuilder().disableHtmlEscaping().create();
@Test
public void testQueryMenuList() {
    // 原始的数据
    List<Menu> rootMenu = menuDao.queryMenuList(null);

    // 查看结果
    for (Menu menu : rootMenu) {
        System.out.println(menu);
    }
    // 最后的结果
    List<Menu> menuList = new ArrayList<Menu>();
    // 先找到所有的一级菜单
    for (int i = 0; i < rootMenu.size(); i++) {
        // 一级菜单没有parentId
        if (StringUtils.isBlank(rootMenu.get(i).getParentId())) {
            menuList.add(rootMenu.get(i));
        }
    }
    // 为一级菜单设置子菜单,getChild是递归调用的
    for (Menu menu : menuList) {
        menu.setChildMenus(getChild(menu.getId(), rootMenu));
    }
    Map<String,Object> jsonMap = new HashMap<>();
    jsonMap.put("menu", menuList);
    System.out.println(gson.toJson(jsonMap));

}

/**
 * 递归查找子菜单
 * 
 * @param id
 *            当前菜单id
 * @param rootMenu
 *            要查找的列表
 * @return
 */
private List<Menu> getChild(String id, List<Menu> rootMenu) {
    // 子菜单
    List<Menu> childList = new ArrayList<>();
    for (Menu menu : rootMenu) {
        // 遍历所有节点,将父菜单id与传过来的id比较
        if (StringUtils.isNotBlank(menu.getParentId())) {
            if (menu.getParentId().equals(id)) {
                childList.add(menu);
            }
        }
    }
    // 把子菜单的子菜单再循环一遍
    for (Menu menu : childList) {// 没有url子菜单还有子菜单
        if (StringUtils.isBlank(menu.getUrl())) {
            // 递归
            menu.setChildMenus(getChild(menu.getId(), rootMenu));
        }
    } // 递归退出条件
    if (childList.size() == 0) {
        return null;
    }
    return childList;
}

menuDao.queryMenuList(null);查找的结果是一条一条的数据

meuDao

package com.jrbac.dao;

import java.util.List;

import com.jrbac.entity.LoginUser;
import com.jrbac.entity.Menu;

public interface MenuDao {

    /**
     * 查找用户的菜单
     * @param loginUser
     * @return
     */
    public List<Menu> queryMenuList(LoginUser loginUser);
}

mybatis

<?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="com.jrbac.dao.MenuDao">
    <select id="queryMenuList" resultType="Menu">
        SELECT 
            id,`name`,parent_id,url,icon,`order`
        FROM 
            jrbac_menu ORDER BY `order` ASC
    </select>
</mapper>

测试程序的运行结果,对输出的 json 进行个格式化后的对比

最终效果

如果你也使用 sbadmin 后台模版的话,它只做到了二级菜单,三级的没有做展开控制。

要做到三级将 sb-admin-2.js 的最后一个替换成下面的。

if (element.is('li')) {
    element.addClass('active');
    element.parent().addClass('in');
}

再奉上前端 jsp 页面输出菜单的代码

<c:forEach items="${userMenuList }" var="menu" varStatus="status">
                    <!-- 一级子菜单没有parentId,有url -->
                    <c:if test="${empty menu.parentId and not empty menu.url}">
        <li>
            <a href="<c:url value='${menu.url }'/>">
                <i class="${menu.icon } fa-fw"></i> ${menu.name }
            </a>
        </li>
    </c:if>
    <!-- 可展开的一级菜单,没有parentId,有url -->
    <c:if test="${empty menu.parentId and empty menu.url}">
        <li>
            <a href="#">
                <i class="${menu.icon } fa-fw"></i> ${menu.name }<span class="fa arrow"></span>
            </a>
            <ul class="nav nav-second-level">
                <!-- 没有url的是三级菜单,有url的直接输出到li中 -->
                <c:forEach items="${menu.childMenus}" var="secondChild" varStatus="status">
                    <c:if test="${not empty secondChild.url }">
                        <li>
                                 <a href="<c:url value='${secondChild.url }'/>">${secondChild.name }</a>
                              </li>
                    </c:if>
                    <!-- 二级菜单url为空,表示还有三级菜单 -->
                    <c:if test="${empty secondChild.url }">
                        <li>
                                 <a href="#">${secondChild.name }<span class="fa arrow"></span></a>
                                 <ul class="nav nav-third-level">
                                        <c:forEach items="${secondChild.childMenus}" var="thirdChild" varStatus="status">
                                        <li>
                                           <a href="<c:url value='${thirdChild.url }'/>">${thirdChild.name }</a>
                                       </li>
                                      </c:forEach>
                                 </ul>
                              </li>
                    </c:if>
                </c:forEach>
            </ul>
        </li>
    </c:if>
</c:forEach>

转载自:http://blog.csdn.net/frankcheng5143/article/details/52958486
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3206 引用 • 8217 回帖
  • 递归
    15 引用 • 9 回帖
  • 菜单
    3 引用 • 5 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • someone

    😄

  • leap

    我也做了个类似的。不过前端是 vue。哈哈😂

  • someone

    😄 😄 😄

推荐标签 标签

  • Gitea

    Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。

    5 引用 • 16 回帖 • 3 关注
  • OneDrive
    2 引用 • 3 关注
  • Access
    1 引用 • 3 回帖 • 14 关注
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    293 引用 • 4496 回帖 • 688 关注
  • Kubernetes

    Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。

    119 引用 • 54 回帖
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    146 引用 • 442 回帖
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 174 关注
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖
  • Outlook
    1 引用 • 5 回帖
  • 面试

    面试造航母,上班拧螺丝。多面试,少加班。

    327 引用 • 1395 回帖
  • CodeMirror
    2 引用 • 17 回帖 • 197 关注
  • 电影

    这是一个不能说的秘密。

    125 引用 • 610 回帖
  • GitLab

    GitLab 是利用 Ruby 一个开源的版本管理系统,实现一个自托管的 Git 项目仓库,可通过 Web 界面操作公开或私有项目。

    46 引用 • 72 回帖
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    10 引用 • 80 回帖 • 1 关注
  • HBase

    HBase 是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的 Google 论文 “Bigtable:一个结构化数据的分布式存储系统”。就像 Bigtable 利用了 Google 文件系统所提供的分布式数据存储一样,HBase 在 Hadoop 之上提供了类似于 Bigtable 的能力。

    17 引用 • 6 回帖 • 72 关注
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    248 引用 • 1342 回帖
  • 支付宝

    支付宝是全球领先的独立第三方支付平台,致力于为广大用户提供安全快速的电子支付/网上支付/安全支付/手机支付体验,及转账收款/水电煤缴费/信用卡还款/AA 收款等生活服务应用。

    29 引用 • 347 回帖 • 2 关注
  • 知乎

    知乎是网络问答社区,连接各行各业的用户。用户分享着彼此的知识、经验和见解,为中文互联网源源不断地提供多种多样的信息。

    10 引用 • 66 回帖
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    110 引用 • 153 回帖
  • WebClipper

    Web Clipper 是一款浏览器剪藏扩展,它可以帮助你把网页内容剪藏到本地。

    3 引用 • 9 回帖 • 3 关注
  • GitHub

    GitHub 于 2008 年上线,目前,除了 Git 代码仓库托管及基本的 Web 管理界面以外,还提供了订阅、讨论组、文本渲染、在线文件编辑器、协作图谱(报表)、代码片段分享(Gist)等功能。正因为这些功能所提供的便利,又经过长期的积累,GitHub 的用户活跃度很高,在开源世界里享有深远的声望,并形成了社交化编程文化(Social Coding)。

    213 引用 • 2044 回帖
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    20 引用 • 245 回帖 • 249 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 740 关注
  • 职场

    找到自己的位置,萌新烦恼少。

    127 引用 • 1708 回帖 • 1 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 724 关注
  • V2Ray
    1 引用 • 15 回帖 • 4 关注
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    59 引用 • 25 回帖 • 5 关注