在网站开发的时候我们会对网站的栏目进行分类,一个栏目可以有多个子分类,一个子分类又可以有分裂,例如:新闻栏目下有每日早报和每日晚报两个栏目,其中每日早报下面又分为上海早报,北京早报,杭州早报,下面是京东首页的分类图。

 

数据库设计
我们在设计数据库的时候仅仅使用一张表就可以把上面的关系给捋清楚,就是通过一个parentid字段,让我们开看一下这张表的表结构
 

各位看官可以看一下建表语句
DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键递增',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '分类名称',
`parentid` int(11) NULL DEFAULT NULL COMMENT '父节点id',
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '分类链接',
`icon` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '分类图标',
`order` int(11) NULL DEFAULT NULL COMMENT '分类排序权重',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

再让我们来插入一点测试数据

INSERT INTO `menu` VALUES (1, '新闻', 0, '/www/xinwen', 'xxx', 1);
INSERT INTO `menu` VALUES (2, '每日日报', 1, '/www/meiriribao', 'xxx', 2);
INSERT INTO `menu` VALUES (3, '每日晚报', 1, '/www/zaobao', 'xxx', 1);
INSERT INTO `menu` VALUES (4, '河南日报', 2, '/www/henan', 'xxx', 2);
INSERT INTO `menu` VALUES (5, '上海日报', 2, '/www/shanghai', 'xxx', 1);
INSERT INTO `menu` VALUES (6, '南京日报', 2, '/www/nanjing', 'xxx', 3);
INSERT INTO `menu` VALUES (7, '开封日报', 4, '/www/zhoukou', 'xxx', 2);
INSERT INTO `menu` VALUES (8, '郑州日报', 4, '/www/zhenghzou', 'xxx', 3);
 现在我们来看一下Java程序中该如何从数据库中读取数据这样的数据返回页面。在下在项目中用的是SSM框架因为并不是SSM框架教程,这里仅仅贴出Dao层,简单至极
List<Menu> findAll();
 <select id="findAll" resultMap="BaseResultMap">
select * from menu;
</select>
看一下菜单对应的实体

package com.sys.domain;

import java.util.List;

public class Menu implements Comparable<Menu> {
private Integer id; private String name; private Integer parentid; private String url; private String icon; private Integer order; //子菜单列表
private List<Menu> children; public List<Menu> getChildren() {
return children;
}
public void setChildren(List<Menu> children) {
this.children = children;
} public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name == null ? null : name.trim();
} public Integer getParentid() {
return parentid;
} public void setParentid(Integer parentid) {
this.parentid = parentid;
} public String getUrl() {
return url;
} public void setUrl(String url) {
this.url = url == null ? null : url.trim();
} public String getIcon() {
return icon;
} public void setIcon(String icon) {
this.icon = icon == null ? null : icon.trim();
} public Integer getOrder() {
return order;
} public void setOrder(Integer order) {
this.order = order;
} @Override
public int compareTo(Menu o) {
if (this.order != o.order) {
return this.order - o.order;
}
return 0;
}
}

实体关键点1:实体Menu实现了Comparable接口并实现了compareTo方法

这样就可以直接使用Collections.sort()方法进行List排序

实体关键点2:实体中新增一个属性List<Menu> children,用于存储返回页面的子节点

 

实体较介绍完了,我们就可以直接看代码啦,都加上注释了很简单

package com.sys.menutree;

import com.sys.dao.MenuMapper;
import com.sys.domain.Menu;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody; import java.util.*; /**
* @Author:jimisun
* @Description:
* @Date:Created in 18:02 2018/8/8
* @Modified By:
*/
@Controller
@RequestMapping("/menu")
public class MenuController { @Autowired
private MenuMapper menuMapper; @RequestMapping(value = "/findTree", method = RequestMethod.POST)
@ResponseBody
public Map<String, Object> findTree(@RequestBody(required = false) Menu menu) {
//构建返回数据
Map<String, Object> data = new HashMap<String, Object>(); try {
//查询到的所有菜单
List<Menu> allMenu = menuMapper.findAll();
//根节点
List<Menu> rootMenu = new ArrayList<Menu>(); //根据传递的参数设置根节点
if (menu != null && menu.getId() != null) {
//父节点为传递的id为根节点
for (Menu nav : allMenu) {
if (nav.getParentid().equals(menu.getId())) {
rootMenu.add(nav);
}
}
} else {
//父节点是0的,为根节点。
for (Menu nav : allMenu) {
if (nav.getParentid().equals(0)) {
rootMenu.add(nav);
}
}
} // 根据Menu类的order排序
Collections.sort(rootMenu); //为根菜单设置子菜单,getClild是递归调用的
for (Menu nav : rootMenu) {
//获取根节点下的所有子节点 使用getChild方法
List<Menu> childList = getChild(nav.getId(), allMenu);
//给根节点设置子节点
nav.setChildren(childList);
} data.put("success", "true");
data.put("list", rootMenu);
return data;
} catch (Exception e) {
data.put("success", "false");
data.put("list", new ArrayList());
return data;
} } /**
* 递归设置栏目的子节点
*
* @param id 父节点id
* @param allMenu 节点列表
* @return
*/
private List<Menu> getChild(Integer id, List<Menu> allMenu) {
//子菜单
List<Menu> childList = new ArrayList<Menu>();
for (Menu nav : allMenu) {
// 遍历所有节点,将所有菜单的父id与传过来的根节点的id比较
//相等说明:为该根节点的子节点。
if (nav.getParentid().equals(id)) {
childList.add(nav);
}
}
//递归设置子节点
for (Menu nav : childList) {
nav.setChildren(getChild(nav.getId(), allMenu));
}
//排序
Collections.sort(childList);
//如果节点下没有子节点,返回一个空List(递归退出)
if (childList.size() == 0) {
return new ArrayList<Menu>();
}
return childList;
} }

最后我们来看一下功能演示

直接发送直接请求接口,获取的是所有分类

请求结果

{
"success": "true",
"list": [
{
"id": 5,
"name": "上海日报",
"parentid": 2,
"url": "/www/shanghai",
"icon": "xxx",
"order": 1,
"children": []
},
{
"id": 4,
"name": "河南日报",
"parentid": 2,
"url": "/www/henan",
"icon": "xxx",
"order": 2,
"children": [
{
"id": 7,
"name": "开封日报",
"parentid": 4,
"url": "/www/zhoukou",
"icon": "xxx",
"order": 2,
"children": []
},
{
"id": 8,
"name": "郑州日报",
"parentid": 4,
"url": "/www/zhenghzou",
"icon": "xxx",
"order": 3,
"children": []
}
]
},
{
"id": 6,
"name": "南京日报",
"parentid": 2,
"url": "/www/nanjing",
"icon": "xxx",
"order": 3,
"children": []
}
]
}

发送请求接口,并附带要查询的分类id

结果如下

{
"success": "true",
"list": [
{
"id": 5,
"name": "上海日报",
"parentid": 2,
"url": "/www/shanghai",
"icon": "xxx",
"order": 1,
"children": []
},
{
"id": 4,
"name": "河南日报",
"parentid": 2,
"url": "/www/henan",
"icon": "xxx",
"order": 2,
"children": [
{
"id": 7,
"name": "开封日报",
"parentid": 4,
"url": "/www/zhoukou",
"icon": "xxx",
"order": 2,
"children": []
},
{
"id": 8,
"name": "郑州日报",
"parentid": 4,
"url": "/www/zhenghzou",
"icon": "xxx",
"order": 3,
"children": []
}
]
},
{
"id": 6,
"name": "南京日报",
"parentid": 2,
"url": "/www/nanjing",
"icon": "xxx",
"order": 3,
"children": []
}
]
}

Java构建网站多级菜单功能解决方案的更多相关文章

  1. java实现网站paypal支付功能并且异步修改订单的状态

    java实现网站paypal支付功能并且异步修改订单的状态:步骤如下 第一步:去paypal的官网https://www.paypal.com注册一个个人账号,在创建沙箱测试账号时需要用到 第二步:p ...

  2. java生成多级菜单树

    使用java实现一个多级菜单树结构 先上数据库 ps_pid字段很重要,是父级菜单的id Menu类 Menu类要新增一个字段,用来存放子菜单 /** * 子菜单列表 */ private List& ...

  3. Python简单实现多级菜单

    # -*- coding: utf-8 -*- # @Time : 2018-06-01 13:40 # @Author : 超人 # @Email : huxiaojiu111@gmail.com ...

  4. Atitit.excel导出 功能解决方案 php java C#.net版总集合.doc

    Atitit.excel导出 功能解决方案 php java C#.net版总集合.docx 1.1. Excel的保存格式office2003 office2007/2010格式1 1.2. 类库选 ...

  5. java 24 - 7 GUI之 创建多级菜单窗体

    需求: 创建多级菜单 步骤: A:创建窗体对象(并设置属性和布局) B:创建菜单栏 C:创建菜单和子菜单 D:逐步添加菜单(子菜单添加到菜单中,菜单添加到菜单栏中) E:窗体中设置菜单栏(菜单栏并不是 ...

  6. Vue2 实现树形菜单(多级菜单)功能模块

    结构示意图 ├── index.html ├── main.js ├── router │ └── index.js # 路由配置文件 ├── components # 组件目录 │ ├── App. ...

  7. 网站开发---js与java实现的一些小功能

    记录一下网站开发过程中的一些小功能 1.js获取当前年份: <span>Copyright © 2017-<script>document.write( new Date(). ...

  8. 使用Java语言开发微信公众平台(八)——自定义菜单功能

    随着上一篇文章的结束,我们已经实现了所有消息的类型的回复功能.今天,我们来学习更加高大上,也更加重要的自定义菜单功能. 一.了解自定义菜单 自定义菜单是微信公众平台最常用也是最重要的功能之一.根据微信 ...

  9. Java中各类Cache机制实现解决方案[来自CSDN]

    摘要:在Java中,不同的类都有自己单独的Cache机制,实现的方法也可能有所不同,文章列举了Java中常见的各类Cache机制的实现方法,同时进行了综合的比较. 在Java中,不同的类都有自己单独的 ...

随机推荐

  1. js操作cookie的一些注意项

     这两天做购物车逻辑.依照通常的做法,把预购信息存放在cookie里,结果发生了非常多不可理喻的事情,完整的证明了我对cookie的无知. . . 这么多年.非常少用cookie,由于认为它不安全 ...

  2. struts2设置加载非默认路径的struts.xml文件解决方案

    方案一:   首先我们要明白struts2加载配置文件都是从它自己的jar包和\WEB-INF\classes两个默认的位置加载的,如果你想改变strusts2中的文件的默认加载路径,可以在web项目 ...

  3. 李洪强iOS经典面试题37-解释垃圾回收的原理

    李洪强iOS经典面试题37-解释垃圾回收的原理   问题 我们知道,Android 手机通常使用 Java 来开发,而 Java 是使用垃圾回收这种内存管理方式. 那么,ARC 和垃圾回收对比,有什么 ...

  4. (3)FluidMoveBehavior 之模仿 Windows Phone 开始菜单的 Tile 长按后排序

    这个工程和上一篇 (2)中介绍的排序大同小异,只是比上一篇交换复杂一点,不是通过单击进行交换, 而是拖动一个 Tile 到另一个 Tile 上时,判断两个 Tile 的中心距离是否符合条件来判断是否进 ...

  5. Nginx 常见问题解决

    如果提示端口已经被占用: probably another instance of uWSGI is running on the same address (:8002). bind(): Addr ...

  6. CodeForces 459D Pashmak and Parmida's problem

    Pashmak and Parmida's problem Time Limit:3000MS     Memory Limit:262144KB     64bit IO Format:%I64d ...

  7. Redis的字典扩容与ConcurrentHashMap的扩容策略比较

    本文介绍Redis的字典(是种Map)扩容与ConcurrentHashMap的扩容策略,并比较它们的优缺点. (不讨论它们的实现细节) 首先Redis的字典采用的是一种‘’单线程渐进式rehash‘ ...

  8. jquery checkbox选中

    楼主写的在1.6之前是没有问题的,jquery 1.6后就要这样写了,<input type='checkbox' id='cb'/> <script> //获取是否选中 va ...

  9. sama5 kio接口控制

    //example #include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <str ...

  10. JavaScript实现网页安全登录(转)

    现在很多商业网站的用户登录都是明码传输的,而一般用户又习惯于所有帐号使用相同的密码来保存,甚至很多人使用的密码和自己的银行帐号都一样哦!所 以嘛还是有一定的安全隐患的,YAHOO的免费邮箱登录使用了M ...