查詢菜單的時候,我們可能會想直接從數據庫中查詢出菜單,但是像京東的首頁菜單是這樣的:

數據庫設計
我們在設計數據庫的時候僅僅使用一張表就可以把上面的關系給捋清楚,就是通過一個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);
實體類
import lombok.Data;
import java.io.Serializable;
import java.util.List;
@Data
public class Menu implements Comparable<Menu>, Serializable {
private static final long serialVersionUID = -4866555859812317355L;
private Integer id;
private String name;
private Integer parentId;
private String url;
private String icon;
private Integer order;
//子菜單列表
private List<Menu> children;
@Override
public int compareTo(Menu o) {
if(this.order != o.order){
return this.order - o.order;
}
return 0;
}
}
先看看controller層:
@Autowired
private MenuService menuService;
@RequestMapping(value = "/menu/findTree",method = RequestMethod.POST)
public BaseResponse create(@RequestBody(required = false) Menu menu) {
List<Menu> list = menuService.findAll(menu);
return BaseResponse.ok(list);
}
service接口
import com.free.freedom.entity.Menu;
import java.util.List;
public interface MenuService {
List<Menu> findAll(Menu menu);
}
實現類
import com.free.freedom.entity.Menu;
import com.free.freedom.mapper.MenuMapper;
import com.free.freedom.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@Service
public class MenuServiceImpl implements MenuService {
@Autowired
private MenuMapper menuMapper;
@Override
public List<Menu> findAll(Menu menu) {
try {
//查詢出所有的菜單
List<Menu> allMenu = menuMapper.findAll();
//根節點存儲
List<Menu> rootMenu = new ArrayList<>();
//根據傳遞的參數設置根節點
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);
//為根節點設置子菜單,getChild是遞歸調用
for (Menu nv : rootMenu) {
//獲取根節點下的所有子節點,使用getChild方法
List<Menu> childList = getChild(nv.getId(),allMenu);
//給根節點設置子節點
nv.setChildren(childList);
}
return rootMenu;
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
}
private List<Menu> getChild(Integer id, List<Menu> allMenu) {
//子菜單
List<Menu> childList = new ArrayList<>();
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;
}
}
mapper層
import com.free.freedom.entity.Menu;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface MenuMapper {
List<Menu> findAll();
}
mapper.xml
<?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.free.freedom.mapper.MenuMapper">
<resultMap id="BaseResultMap" type="com.free.freedom.entity.Menu">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="name" jdbcType="VARCHAR" property="name" />
<result column="parentid" jdbcType="INTEGER" property="parentId" />
<result column="url" jdbcType="VARCHAR" property="url" />
<result column="icon" jdbcType="VARCHAR" property="icon" />
<result column="order" jdbcType="INTEGER" property="order" />
</resultMap>
<select id="findAll" resultMap="BaseResultMap">
select * from menu
</select>
</mapper>
測試結果

{
"status": 0,
"msg": "OK",
"data": [
{
"id": 1,
"name": "新聞",
"parentId": 0,
"url": "/www/xinwen",
"icon": "xxx",
"order": 1,
"children": [
{
"id": 3,
"name": "每日晚報",
"parentId": 1,
"url": "/www/zaobao",
"icon": "xxx",
"order": 1,
"children": []
},
{
"id": 2,
"name": "每日日報",
"parentId": 1,
"url": "/www/meiriribao",
"icon": "xxx",
"order": 2,
"children": [
{
"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": 9,
"name": "聯播",
"parentId": 0,
"url": "/www/xinwen",
"icon": "xxx",
"order": 1,
"children": []
}
]
}

{
"status": 0,
"msg": "OK",
"data": [
{
"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": []
}
]
}
