1.目的:一个网站的菜单实现,存在多级关系,返回一个多级的树状结构,供前端使用。
2.整体思路 a.将所有对象查回来作为一个列表然后对他树状进行处理
b.通过vo类直接返回个树状结构
两者达成的效果一样,只是实现的地方不同
3.实现
a。根据常规场景生成一个菜单表(mysql)
CREATE TABLE `menu` (
`menu_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '菜单id',
`menu_name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '菜单名称',
`menu_path` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '菜单路径',
`menu_level` bigint(20) DEFAULT NULL COMMENT '菜单等级',
`parent_id` bigint(20) DEFAULT NULL COMMENT '父id',
`menu_title` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '菜单备注',
PRIMARY KEY (`menu_id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
b.使用反向生成生成dao,enetity和mapper
c.建立Controller
package com.example.demo.controller;
import com.example.demo.enetity.ResponseBean;
import com.example.demo.service.MenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@CrossOrigin
@RestController
@RequestMapping("/menu")
public class MenuController {
@Autowired
private MenuService menuService;
@GetMapping(value = "/load")
public ResponseBean loadMusicList() {
return menuService.uploadMenu1();
}
@GetMapping(value = "/load/ver2")
public ResponseBean loadMusicList2() {
return menuService.uploadMenu2();
}
}
e.service
package com.example.demo.service;
import com.example.demo.enetity.ResponseBean;
public interface MenuService {
ResponseBean uploadMenu1();
ResponseBean uploadMenu2();
}
f.
1。实现类实现
@Autowired
private MenuMapper menuMapper;
@Override
public ResponseBean uploadMenu1() {
List<Menu> menus = menuMapper.selectAll();
List<Map> list = treeMenu(menus);
JSONObject jsonObject = new JSONObject();
jsonObject.put("list", list);
return CommonUtils.succssonJson(jsonObject);
}
private List<Map> treeMenu(List<Menu> menus) {
List<Map> list1 = new ArrayList<>();
Long maxLevel = 0L;
Map mapRelation = new HashMap<>();
Map mapObject = new HashMap<>();
Map mapLoaction = new HashMap<>();
for (int i = 0; i < menus.size(); i++) {
List<Map> children = new ArrayList<>();
Map map = new HashMap<>();
map.put("id", Long.valueOf(menus.get(i).getMenuId()));
map.put("menuLevel", menus.get(i).getMenuLevel());
map.put("menuName", menus.get(i).getMenuName());
map.put("menuPath", menus.get(i).getMenuPath());
map.put("menuTitle", menus.get(i).getMenuTitle());
map.put("parentId", menus.get(i).getParentId());
map.put("children", children);
if (menus.get(i).getMenuLevel() != null && menus.get(i).getMenuLevel() > maxLevel) {
maxLevel = menus.get(i).getMenuLevel();
}
list1.add(map);
mapRelation.put(menus.get(i).getMenuId(), menus.get(i).getParentId());
mapObject.put(menus.get(i).getMenuId(), menus.get(i));
mapLoaction.put(menus.get(i).getMenuId(), i);
}
List list = new ArrayList<>();
//数据拼装
for (int j = new Long(maxLevel).intValue(); j > 1; j--) {
Set set = mapRelation.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
String s = iterator.next().toString();
String[] split = s.split("=");
String s1 = split[0];
Menu menu = (Menu) mapObject.get(Long.parseLong(s1));
if (menu.getMenuLevel() != null && menu.getMenuLevel() == j) {
Map mapParent = list1.get((int) mapLoaction.get(menu.getParentId()));
Map mapChild = list1.get((int) mapLoaction.get(menu.getMenuId()));
List<Map> childrenList = (List<Map>) mapParent.get("children");
childrenList.add(mapChild);
mapParent.put("children", childrenList);
}
}
}
//只取出一级标题
for (int m = 0; m < list1.size(); m++) {
String menuLevel = list1.get(m).get("menuLevel").toString();
if (menuLevel.equals("1")) {
list.add(list1.get(m));
}
}
return list;
}
对应的sql是
<resultMap id="BaseResultMap" type="com.example.demo.enetity.Menu">
<id column="menu_id" jdbcType="BIGINT" property="menuId"/>
<result column="menu_name" jdbcType="VARCHAR" property="menuName"/>
<result column="menu_path" jdbcType="VARCHAR" property="menuPath"/>
<result column="menu_level" jdbcType="BIGINT" property="menuLevel"/>
<result column="parent_id" jdbcType="BIGINT" property="parentId"/>
<result column="menu_title" jdbcType="VARCHAR" property="menuTitle"/>
</resultMap>
<select id="selectAll" resultMap="BaseResultMap">
select
menu_id, menu_name, menu_path, menu_level, parent_id, menu_title
from menu
</select>
这种方法实现sql很简单,实现有点复杂,大概就是,盖房子一样,将第五级盖到第四级,然后第四级盖到第三级
知道第一级,然后最后取出层级为最高级的就可以啦。
2.sql实现
实现类就很简单
@Override
public ResponseBean uploadMenu2() {
//需要新建一个vo类实现
List<menuVo> menus = menuMapper.selectTreeList();
JSONObject jsonObject = new JSONObject();
jsonObject.put("list", menus);
return CommonUtils.succssonJson(jsonObject);
}
但是需要再新建一个vo对象,他比正常的对象多了一个children,用来存放下一级别的数据
package com.example.demo.VO;
import java.util.List;
public class menuVo {
public List<menuVo> getChildren() {
return children;
}
public void setChildren(List<menuVo> children) {
this.children = children;
}
private List<menuVo> children;
/**
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column menu.menu_id
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
private Long menuId;
/**
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column menu.menu_name
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
private String menuName;
/**
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column menu.menu_path
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
private String menuPath;
/**
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column menu.menu_level
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
private Long menuLevel;
/**
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column menu.parent_id
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
private Long parentId;
/**
*
* This field was generated by MyBatis Generator.
* This field corresponds to the database column menu.menu_title
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
private String menuTitle;
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column menu.menu_id
*
* @return the value of menu.menu_id
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public Long getMenuId() {
return menuId;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column menu.menu_id
*
* @param menuId the value for menu.menu_id
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public void setMenuId(Long menuId) {
this.menuId = menuId;
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column menu.menu_name
*
* @return the value of menu.menu_name
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public String getMenuName() {
return menuName;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column menu.menu_name
*
* @param menuName the value for menu.menu_name
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public void setMenuName(String menuName) {
this.menuName = menuName == null ? null : menuName.trim();
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column menu.menu_path
*
* @return the value of menu.menu_path
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public String getMenuPath() {
return menuPath;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column menu.menu_path
*
* @param menuPath the value for menu.menu_path
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public void setMenuPath(String menuPath) {
this.menuPath = menuPath == null ? null : menuPath.trim();
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column menu.menu_level
*
* @return the value of menu.menu_level
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public Long getMenuLevel() {
return menuLevel;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column menu.menu_level
*
* @param menuLevel the value for menu.menu_level
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public void setMenuLevel(Long menuLevel) {
this.menuLevel = menuLevel;
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column menu.parent_id
*
* @return the value of menu.parent_id
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public Long getParentId() {
return parentId;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column menu.parent_id
*
* @param parentId the value for menu.parent_id
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public void setParentId(Long parentId) {
this.parentId = parentId;
}
/**
* This method was generated by MyBatis Generator.
* This method returns the value of the database column menu.menu_title
*
* @return the value of menu.menu_title
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public String getMenuTitle() {
return menuTitle;
}
/**
* This method was generated by MyBatis Generator.
* This method sets the value of the database column menu.menu_title
*
* @param menuTitle the value for menu.menu_title
*
* @mbg.generated Mon Dec 30 15:20:24 CST 2019
*/
public void setMenuTitle(String menuTitle) {
this.menuTitle = menuTitle == null ? null : menuTitle.trim();
}
}
sql就很多了,而且每多一个级就要加一次
<select id="selectAll" resultMap="BaseResultMap">
select
menu_id, menu_name, menu_path, menu_level, parent_id, menu_title
from menu
</select>
<resultMap id="treeList" type="com.example.demo.VO.menuVo">
<id column="fir_menu_id" jdbcType="BIGINT" property="menuId"/>
<result column="fir_menu_name" jdbcType="VARCHAR" property="menuName"/>
<result column="fir_menu_path" jdbcType="VARCHAR" property="menuPath"/>
<result column="fir_menu_level" jdbcType="BIGINT" property="menuLevel"/>
<result column="fir_parent_id" jdbcType="BIGINT" property="parentId"/>
<result column="fir_menu_title" jdbcType="VARCHAR" property="menuTitle"/>
<collection property="children" ofType="com.example.demo.VO.menuVo">
<id column="sec_menu_id" jdbcType="BIGINT" property="menuId"/>
<result column="sec_menu_name" jdbcType="VARCHAR" property="menuName"/>
<result column="sec_menu_path" jdbcType="VARCHAR" property="menuPath"/>
<result column="sec_menu_level" jdbcType="BIGINT" property="menuLevel"/>
<result column="sec_parent_id" jdbcType="BIGINT" property="parentId"/>
<result column="sec_menu_title" jdbcType="VARCHAR" property="menuTitle"/>
<collection property="children" ofType="com.example.demo.VO.menuVo">
<id column="thi_menu_id" jdbcType="BIGINT" property="menuId"/>
<result column="thi_menu_name" jdbcType="VARCHAR" property="menuName"/>
<result column="thi_menu_path" jdbcType="VARCHAR" property="menuPath"/>
<result column="thi_menu_level" jdbcType="BIGINT" property="menuLevel"/>
<result column="thi_parent_id" jdbcType="BIGINT" property="parentId"/>
<result column="thi_menu_title" jdbcType="VARCHAR" property="menuTitle"/>
<collection property="children" ofType="com.example.demo.VO.menuVo">
<id column="four_menu_id" jdbcType="BIGINT" property="menuId"/>
<result column="four_menu_name" jdbcType="VARCHAR" property="menuName"/>
<result column="four_menu_path" jdbcType="VARCHAR" property="menuPath"/>
<result column="four_menu_level" jdbcType="BIGINT" property="menuLevel"/>
<result column="four_parent_id" jdbcType="BIGINT" property="parentId"/>
<result column="four_menu_title" jdbcType="VARCHAR" property="menuTitle"/>
<collection property="children" ofType="com.example.demo.VO.menuVo">
<id column="five_menu_id" jdbcType="BIGINT" property="menuId"/>
<result column="five_menu_name" jdbcType="VARCHAR" property="menuName"/>
<result column="five_menu_path" jdbcType="VARCHAR" property="menuPath"/>
<result column="five_menu_level" jdbcType="BIGINT" property="menuLevel"/>
<result column="five_parent_id" jdbcType="BIGINT" property="parentId"/>
<result column="five_menu_title" jdbcType="VARCHAR" property="menuTitle"/>
</collection>
</collection>
</collection>
</collection>
</resultMap>
h:测试
导入数据
insert into `menu`(`menu_id`,`menu_name`,`menu_path`,`menu_level`,`parent_id`,`menu_title`) values (1,'一级菜单1','a',1,NULL,'下面有三个节点'),(2,'一级菜单2','b',1,NULL,'下面有零个节点'),(3,'一级菜单3','c',1,NULL,'下面有两个节点'),(4,'二级菜单1','d',2,1,'下面有一个节点'),(5,'二级菜单2','1',2,1,'下面有一个节点'),(6,'二级菜单3','2',2,3,'下面有0个节点'),(7,'二级菜单4','3',2,1,'下面有一个节点'),(8,'二级菜单5','4',2,3,'下面有一个节点'),(9,'三级菜单1','5',3,4,'下面有0个节点'),(10,'三级菜单2','q',3,5,'下面有0个节点'),(11,'三级菜单3','qe',3,7,'下面有2个节点'),(12,'三级菜单4','s',3,8,'下面有0个节点'),(13,'四季菜单1','s',4,11,'下面有1个节点'),(14,'四季菜单2','a',4,11,'下面有0个节点'),(15,'五级菜单','f',5,13,'下面有0个节点'),(16,'五级菜单1','11',5,14,'下面有0个节点');
然后postman发请求了localhost:8090/menu/load和localhost:8090/menu/load/ver2
然后就可以看结果了
a json数据{ "retCode": "1", "retMsg": "请求成功", "result": { "list": [ { "menuPath": "a", "children": [ { "menuPath": "d", "children": [ { "menuPath": "5", "children": [], "menuTitle": "下面有0个节点", "menuLevel": 3, "menuName": "三级菜单1", "id": 9, "parentId": 4 } ], "menuTitle": "下面有一个节点", "menuLevel": 2, "menuName": "二级菜单1", "id": 4, "parentId": 1 }, { "menuPath": "1", "children": [ { "menuPath": "q", "children": [], "menuTitle": "下面有0个节点", "menuLevel": 3, "menuName": "三级菜单2", "id": 10, "parentId": 5 } ], "menuTitle": "下面有一个节点", "menuLevel": 2, "menuName": "二级菜单2", "id": 5, "parentId": 1 }, { "menuPath": "3", "children": [ { "menuPath": "qe", "children": [ { "menuPath": "s", "children": [ { "menuPath": "f", "children": [], "menuTitle": "下面有0个节点", "menuLevel": 5, "menuName": "五级菜单", "id": 15, "parentId": 13 } ], "menuTitle": "下面有1个节点", "menuLevel": 4, "menuName": "四季菜单1", "id": 13, "parentId": 11 }, { "menuPath": "a", "children": [ { "menuPath": "11", "children": [], "menuTitle": "下面有0个节点", "menuLevel": 5, "menuName": "五级菜单1", "id": 16, "parentId": 14 } ], "menuTitle": "下面有0个节点", "menuLevel": 4, "menuName": "四季菜单2", "id": 14, "parentId": 11 } ], "menuTitle": "下面有2个节点", "menuLevel": 3, "menuName": "三级菜单3", "id": 11, "parentId": 7 } ], "menuTitle": "下面有一个节点", "menuLevel": 2, "menuName": "二级菜单4", "id": 7, "parentId": 1 } ], "menuTitle": "下面有三个节点", "menuLevel": 1, "menuName": "一级菜单1", "id": 1, "parentId": null }, { "menuPath": "b", "children": [], "menuTitle": "下面有零个节点", "menuLevel": 1, "menuName": "一级菜单2", "id": 2, "parentId": null }, { "menuPath": "c", "children": [ { "menuPath": "2", "children": [], "menuTitle": "下面有0个节点", "menuLevel": 2, "menuName": "二级菜单3", "id": 6, "parentId": 3 }, { "menuPath": "4", "children": [ { "menuPath": "s", "children": [], "menuTitle": "下面有0个节点", "menuLevel": 3, "menuName": "三级菜单4", "id": 12, "parentId": 8 } ], "menuTitle": "下面有一个节点", "menuLevel": 2, "menuName": "二级菜单5", "id": 8, "parentId": 3 } ], "menuTitle": "下面有两个节点", "menuLevel": 1, "menuName": "一级菜单3", "id": 3, "parentId": null } ] } }
b.json数据{ "retCode": "1", "retMsg": "请求成功", "result": { "list": [ { "children": [ { "children": [ { "children": [ { "children": [ { "children": null, "menuId": 15, "menuName": "五级菜单", "menuPath": "f", "menuLevel": 5, "parentId": 13, "menuTitle": "下面有0个节点" } ], "menuId": 13, "menuName": "四季菜单1", "menuPath": "s", "menuLevel": 4, "parentId": 11, "menuTitle": "下面有1个节点" }, { "children": [ { "children": null, "menuId": 16, "menuName": "五级菜单1", "menuPath": "11", "menuLevel": 5, "parentId": 14, "menuTitle": "下面有0个节点" } ], "menuId": 14, "menuName": "四季菜单2", "menuPath": "a", "menuLevel": 4, "parentId": 11, "menuTitle": "下面有0个节点" } ], "menuId": 11, "menuName": "三级菜单3", "menuPath": "qe", "menuLevel": 3, "parentId": 7, "menuTitle": "下面有2个节点" } ], "menuId": 7, "menuName": "二级菜单4", "menuPath": "3", "menuLevel": 2, "parentId": 1, "menuTitle": "下面有一个节点" }, { "children": [ { "children": [], "menuId": 9, "menuName": "三级菜单1", "menuPath": "5", "menuLevel": 3, "parentId": 4, "menuTitle": "下面有0个节点" } ], "menuId": 4, "menuName": "二级菜单1", "menuPath": "d", "menuLevel": 2, "parentId": 1, "menuTitle": "下面有一个节点" }, { "children": [ { "children": [], "menuId": 10, "menuName": "三级菜单2", "menuPath": "q", "menuLevel": 3, "parentId": 5, "menuTitle": "下面有0个节点" } ], "menuId": 5, "menuName": "二级菜单2", "menuPath": "1", "menuLevel": 2, "parentId": 1, "menuTitle": "下面有一个节点" } ], "menuId": 1, "menuName": "一级菜单1", "menuPath": "a", "menuLevel": 1, "parentId": null, "menuTitle": "下面有三个节点" }, { "children": [ { "children": [ { "children": [], "menuId": 12, "menuName": "三级菜单4", "menuPath": "s", "menuLevel": 3, "parentId": 8, "menuTitle": "下面有0个节点" } ], "menuId": 8, "menuName": "二级菜单5", "menuPath": "4", "menuLevel": 2, "parentId": 3, "menuTitle": "下面有一个节点" }, { "children": [], "menuId": 6, "menuName": "二级菜单3", "menuPath": "2", "menuLevel": 2, "parentId": 3, "menuTitle": "下面有0个节点" } ], "menuId": 3, "menuName": "一级菜单3", "menuPath": "c", "menuLevel": 1, "parentId": null, "menuTitle": "下面有两个节点" }, { "children": [], "menuId": 2, "menuName": "一级菜单2", "menuPath": "b", "menuLevel": 1, "parentId": null, "menuTitle": "下面有零个节点" } ] } }
到这里就全部做完了。
有人问我公共类方法是什么,我就分享下吧。
package com.example.demo.util;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.enetity.ResponseBean;
public class CommonUtils {
public static final String SUCCESS_CODE = "1";
public static final String SUCCESS_MSG = "请求成功";
public static ResponseBean succssonJson(JSONObject jsonObject) {
ResponseBean responseBean = new ResponseBean();
responseBean.setRetCode(SUCCESS_CODE);
responseBean.setRetMsg(SUCCESS_MSG);
responseBean.setResult(jsonObject);
return responseBean;
}
public static ResponseBean succssonJson() {
return succssonJson(new JSONObject());
}
public static ResponseBean errorJson(JSONObject jsonObject) {
ResponseBean responseBean = new ResponseBean();
responseBean.setRetCode("0");
responseBean.setRetMsg("请求失败");
return responseBean;
}
public static ResponseBean errorJson() {
return errorJson(new JSONObject());
}
}
公共的实体类
package com.example.demo.enetity;
import com.alibaba.fastjson.JSONObject;
import com.example.demo.util.ErrorEnum;
public class ResponseBean {
private String retCode;
private String retMsg;
private JSONObject result;
public ResponseBean(){
}
public ResponseBean(String retCode, String retMsg, JSONObject result) {
this.retCode = retCode;
this.retMsg = retMsg;
this.result = result;
}
public ResponseBean(String retCode, String retMsg) {
this.retCode = retCode;
this.retMsg = retMsg;
}
public ResponseBean(ErrorEnum errorEnum) {
this.retCode = errorEnum.getErrorCode();
this.retMsg = errorEnum.getErrorMsg();
this.result = new JSONObject();
}
public String getRetCode() {
return retCode;
}
public String getRetMsg() {
return retMsg;
}
public void setRetMsg(String retMsg) {
this.retMsg = retMsg;
}
public JSONObject getResult() {
return result;
}
public void setResult(JSONObject result) {
this.result = result;
}
public void setRetCode(String retCode) {
this.retCode = retCode;
}
}
方法一是自己实现的,需要对逻辑很清楚,循环也比较多比较慢,但是一劳永逸了,写一次这个方法只要保证数据没问题就不会报错。
方法二通过vo接收,实现类就很简单但是sql就多表联了,而且层级是写死的,每次加一个级别的时候就需要改下sql了,但是代码看起来很清楚,各有优劣吧。
来源:CSDN
作者:偶走路带风
链接:https://blog.csdn.net/weixin_45214099/article/details/103767001