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

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

首先看一下菜单的样子

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

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 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3168 引用 • 8207 回帖
  • 递归
    15 引用 • 9 回帖
  • 菜单
    3 引用 • 5 回帖

相关帖子

欢迎来到这里!

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

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

    😄

  • leap

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

  • someone

    😄 😄 😄

推荐标签 标签

  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • VirtualBox

    VirtualBox 是一款开源虚拟机软件,最早由德国 Innotek 公司开发,由 Sun Microsystems 公司出品的软件,使用 Qt 编写,在 Sun 被 Oracle 收购后正式更名成 Oracle VM VirtualBox。

    10 引用 • 2 回帖 • 6 关注
  • ReactiveX

    ReactiveX 是一个专注于异步编程与控制可观察数据(或者事件)流的 API。它组合了观察者模式,迭代器模式和函数式编程的优秀思想。

    1 引用 • 2 回帖 • 126 关注
  • 域名

    域名(Domain Name),简称域名、网域,是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。

    43 引用 • 208 回帖 • 1 关注
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 21 关注
  • SSL

    SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全(Transport Layer Security,TLS)是为网络通信提供安全及数据完整性的一种安全协议。TLS 与 SSL 在传输层对网络连接进行加密。

    69 引用 • 190 回帖 • 494 关注
  • 架构

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

    140 引用 • 441 回帖
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 369 关注
  • 程序员

    程序员是从事程序开发、程序维护的专业人员。

    532 引用 • 3528 回帖
  • Sym

    Sym 是一款用 Java 实现的现代化社区(论坛/BBS/社交网络/博客)系统平台。

    下一代的社区系统,为未来而构建

    523 引用 • 4581 回帖 • 690 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 642 关注
  • 支付宝

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

    29 引用 • 347 回帖
  • ngrok

    ngrok 是一个反向代理,通过在公共的端点和本地运行的 Web 服务器之间建立一个安全的通道。

    7 引用 • 63 回帖 • 598 关注
  • Caddy

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

    10 引用 • 54 回帖 • 126 关注
  • 心情

    心是产生任何想法的源泉,心本体会陷入到对自己本体不能理解的状态中,因为心能产生任何想法,不能分出对错,不能分出自己。

    59 引用 • 369 回帖
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 396 关注
  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    330 引用 • 614 回帖
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 686 关注
  • Love2D

    Love2D 是一个开源的, 跨平台的 2D 游戏引擎。使用纯 Lua 脚本来进行游戏开发。目前支持的平台有 Windows, Mac OS X, Linux, Android 和 iOS。

    14 引用 • 53 回帖 • 512 关注
  • HTML

    HTML5 是 HTML 下一个的主要修订版本,现在仍处于发展阶段。广义论及 HTML5 时,实际指的是包括 HTML、CSS 和 JavaScript 在内的一套技术组合。

    103 引用 • 294 回帖
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 108 关注
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    85 引用 • 895 回帖
  • DevOps

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

    39 引用 • 24 回帖
  • Hadoop

    Hadoop 是由 Apache 基金会所开发的一个分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。

    82 引用 • 122 回帖 • 620 关注
  • Chrome

    Chrome 又称 Google 浏览器,是一个由谷歌公司开发的网页浏览器。该浏览器是基于其他开源软件所编写,包括 WebKit,目标是提升稳定性、速度和安全性,并创造出简单且有效率的使用者界面。

    60 引用 • 287 回帖
  • 房星科技

    房星网,我们不和没有钱的程序员谈理想,我们要让程序员又有理想又有钱。我们有雄厚的房地产行业线下资源,遍布昆明全城的 100 家门店、四千地产经纪人是我们坚实的后盾。

    6 引用 • 141 回帖 • 559 关注
  • Redis

    Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。从 2010 年 3 月 15 日起,Redis 的开发工作由 VMware 主持。从 2013 年 5 月开始,Redis 的开发由 Pivotal 赞助。

    284 引用 • 247 回帖 • 174 关注