@TableName("tb_menu") public class Menu extends BaseEntity { private static final long serialVersionUID = 1L ; * 菜单ID @TableId private Long menuId; * 菜单名称 private String menuName; * 父菜单名称 @TableField(exist = false) private String parentName; * 父菜单ID private Long parentId; * 显示顺序 private String orderNum; * 路由地址 private String path; * 组件路径 private String component; * 是否为外链(0是 1否) private String isFrame; * 是否缓存(0缓存 1不缓存) private String isCache; * 类型(M目录 C菜单 F按钮) private String menuType; * 显示状态(0显示 1隐藏) private String visible; * 菜单状态(0显示 1隐藏) private String status; * 权限字符串 private String perms; * 菜单图标 private String icon; * 子菜单 @TableField(exist = false) private List<Menu> children = new ArrayList <Menu>();

最重要的还是id、parentId、children属性

最通用的写法(递归)

这种写法毫无节操可言,全部通过数据库递归查询。

  • 首先查到根节点,parent_id = 0
  • 通过根节点id获取到所有一级节点,parent_id = 1
  • 递归获取所有节点的子节点,然后调用setChildren()方法组装数据结构。 好多项目都是用这种方法。
  • 1、递归:

    * 根据父节点的id获取所有子节点 * @param menus * @return private List< Menu > getChildPerm (List<Menu> menus, int parentId) { List< Menu > menuList = new ArrayList<>(); //当parentId = 0时,是根节点; // 获取根节点下的子节点 for (Menu menu : menus) { if (menu.getParentId() == parentId) { Menu menuData = this .recursionTree (menus, menu); menuList .add (menuData); return menuList; * 递归树结构 * @param collect private Menu recursionTree (List<Menu> collect, Menu menu) { List< Menu > childList = this .getChildList (collect, menu); menu .setChildren (childList); for (Menu col : childList) { if (hasChild(collect, col)) { recursionTree (collect, col); return menu ; * 得到子节点列表 private List< Menu > getChildList (List<Menu> list, Menu t) { List< Menu > tlist = new ArrayList< Menu >(); Iterator< Menu > it = list .iterator (); while (it.hasNext()) { Menu n = (Menu) it .next (); if (n.getParentId() .longValue () == t .getMenuId () .longValue ()) { tlist .add (n); return tlist;

    这样比较啰嗦,性能不是很好,逻辑也不是很清楚。

    2、双重循环:

    这种写法比较简单,也是比较容易想到的。通过双重循环确定父子节点的关系。

    * 获取菜单的树状结构 * @return private List< Menu > menuTrees () { List< Menu > list = new ArrayList<>(); List< Menu > menus = menuMapper .selectList (null); for (Menu m : menus) { //获取根节点 if (m.getParentId() .equals ( 0 L)) { list .add (m); for (Menu child : menus) { if (child.getParentId() .equals (m.getMenuId())) { m .addChild (child); return list; //菜单的实体类中加addChild方法 public void addChild (Menu menu) { if (children == null) { children = new ArrayList<>(); children .add (menu);

    3、双重遍历

    第一次遍历借助hashmap存储父节点与子节点的关系,第二次遍历设置子节点,由于map中已经维护好了对应关系所以只需要从map取即可。

    * 双重查询 * @return private List<Menu> menuList() { Map<Long, List<Menu>> menuMap = new HashMap<>() ; List<Menu> menuList = menuMapper.selectList(null) ; menuList.forEach(menu -> { List<Menu> children = menuMap.getOrDefault(menu.getParentId(), new ArrayList<>()) ; children.add(menu) ; menuMap.put(menu.getParentId(), children) ; menuList.forEach(menu -> menu.setChildren(menuMap.get(menu.getMenuId()))) ; List<Menu> result = menuList.stream().filter(v -> v.getParentId().equals( "0" )).collect(Collectors.toList()) ; return result ;

    4、Stream分组

    * Stream分组 @Override public List<Menu> selectMenuTree () { List < Menu > menus = menuMapper .selectList (null); //操作所有菜单数据 Map < Long , List < Menu >> groupMap = menus .stream () .collect (Collectors. groupingBy ( Menu ::getParentId)); menus .forEach (menu -> { menu .setChildren (groupMap. get (menu. getMenuId ())); List < Menu > collect = menus .stream () .filter (menu -> menu. getParentId (). equals ( 0 L)) .collect (Collectors. toList ()); return collect ;

    此方法主要通过 Collectors.groupingBy(Menu::getParentId) 方法对 menus 按照 parentId 进行分组,分组后父节点相同的都放一起了。

    然后再循环 menus ,给其设置children属性。

    执行完成后已经形成了多颗树,最后我们再通过 filter() 方法挑选出根节点的那颗树即可。

    请求后返回的数据集如下:

      "code": 200,
      "data": [
          "searchValue": null,
          "createBy": "admin",
          "createTime": "2021-03-10 16:30:56",
          "updateBy": "",
          "updateTime": null,
          "remark": "系统管理目录",
          "params": null,
          "menuId": 1,
          "menuName": "系统管理",
          "parentName": null,
          "parentId": 0,
          "orderNum": "1",
          "path": "system",
          "component": null,
          "isFrame": "1",
          "isCache": "0",
          "menuType": "M",
          "visible": "0",
          "status": "0",
          "perms": "",
          "icon": "system",
          "children": [
              "searchValue": null,
              "createBy": "admin",
              "createTime": "2021-03-10 16:30:56",
              "updateBy": "",
              "updateTime": null,
              "remark": "用户管理菜单",
              "params": null,
              "menuId": 100,
              "menuName": "用户管理",
              "parentName": null,
              "parentId": 1,
              "orderNum": "1",
              "path": "user",
              "component": "system/user/index",
              "isFrame": "1",
              "isCache": "0",
              "menuType": "C",
              "visible": "0",
              "status": "0",
              "perms": "system:user:list",
              "icon": "user",
              "children": [
                  "searchValue": null,
                  "createBy": "admin",
                  "createTime": "2021-03-10 16:31:00",
                  "updateBy": "",
                  "updateTime": null,
                  "remark": "",
                  "params": null,
                  "menuId": 1001,
                  "menuName": "用户查询",
                  "parentName": null,
                  "parentId": 100,
                  "orderNum": "1",
                  "path": "",
                  "component": "",
                  "isFrame": "1",
                  "isCache": "0",
                  "menuType": "F",
                  "visible": "0",
                  "status": "0",
                  "perms": "system:user:query",
                  "icon": "#",
                  "children": null
                  "searchValue": null,
                  "createBy": "admin",
                  "createTime": "2021-03-10 16:31:00",
                  "updateBy": "",
                  "updateTime": null,
                  "remark": "",
                  "params": null,
                  "menuId": 1002,
                  "menuName": "用户新增",
                  "parentName": null,
                  "parentId": 100,
                  "orderNum": "2",
                  "path": "",
                  "component": "",
                  "isFrame": "1",
                  "isCache": "0",
                  "menuType": "F",
                  "visible": "0",
                  "status": "0",
                  "perms": "system:user:add",
                  "icon": "#",
                  "children": null
                  "searchValue": null,
                  "createBy": "admin",
                  "createTime": "2021-03-10 16:31:01",
                  "updateBy": "",
                  "updateTime": null,
                  "remark": "",
                  "params": null,
                  "menuId": 1003,
                  "menuName": "用户修改",
                  "parentName": null,
                  "parentId": 100,
                  "orderNum": "3",
                  "path": "",
                  "component": "",
                  "isFrame": "1",
                  "isCache": "0",
                  "menuType": "F",
                  "visible": "0",
                  "status": "0",
                  "perms": "system:user:edit",
                  "icon": "#",
                  "children": null
    复制代码
    分类:
    后端
    标签: