java 递归实现权限树(菜单树)

简介: java 递归实现权限树(菜单树)

完整示例代码地址如下:

https://github.com/Dr-Water/springboot-action/tree/master/springboot-shiro


一、 权限树的问题由来

  1. 在开发中难免遇到一个有多级菜单结构树,或者多级部门的结构树,亦或是省市区县的多级结构,数据结构类似如下的json数据:


[
    {
        "id": "1",
        "name": "主菜单1",
        "pid": "0",
        "menuChildren": [
            {
                "id": "4",
                "name": "子菜单1.1",
                "pid": "1",
                "menuChildren": [
                    {
                        "id": "6",
                        "name": "子菜单1.1.1",
                        "pid": "4",
                        "menuChildren": []
                    },
                    {
                        "id": "9",
                        "name": "子菜单1.1.2",
                        "pid": "4",
                        "menuChildren": []
                    }
                ]
            },
            {
                "id": "5",
                "name": "子菜单1.2",
                "pid": "1",
                "menuChildren": []
            }
        ]
    },
    {
        "id": "2",
        "name": "主菜单2",
        "pid": "0",
        "menuChildren": [
            {
                "id": "7",
                "name": "子菜单2.1",
                "pid": "2",
                "menuChildren": []
            },
            {
                "id": "8",
                "name": "子菜单2.2",
                "pid": "2",
                "menuChildren": []
            }
        ]
    },
    {
        "id": "3",
        "name": "主菜单3",
        "pid": "0",
        "menuChildren": []
    }
]

二、 解决方案

目前的解决方案主要有以下两种方案:


  • 方案一:后端把所有需要的数据以一个大list返回前端,前端进行操作,把数据搞成树状结构
  • 方案二: 后端在后端返回数据之前把数据搞成已经有层次结构的数据,方案二也分为两种解决方法
  • 方法一:次性将数据查询出来,在java程序中进行树状结构的构建
  • 方法二: 第一次将最高层次的数据查询出来,然后多次循环查询数据库将子数据查询出来


由于博主的前端水平有限,目前只能用后端的实现方式,再加上每次查询数据库的开销比较大,所以本文使用方案二的方法一进行验证


实现步骤

以菜单的结构树为例


  1. 准备mysql数据库的基础数据



  1. java的实体类:


@Data
@NoArgsConstructor
public class Menu implements Serializable {
    private String  id;
    private String name;
    private String pid;
    private List<Menu> menuChildren;
}


  1. java的dao层


@Mapper
public interface MenuDao {
    /**
     * 根据父类id查询子类菜单
     * @param pid
     * @return
     */
     List<Menu> selectByPid(Integer pid);
    /**
     * 查询所有的菜单
     * @return
     */
     List<Menu> selectAll();
    /**
     * 查询除了一级菜单以外的菜单
     * @return
     */
     List<Menu> selectAllNotBase();
}


  1. mapper文件


<?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.ratel.shiro.dao.MenuDao">
    <select id="selectByPid" resultType="com.ratel.shiro.entity.Menu">
        SELECT * FROM menu WHERE  pid=#{pid}
    </select>
    <select id="selectAll" resultType="com.ratel.shiro.entity.Menu">
        SELECT * FROM menu
    </select>
    <select id="selectAllNotBase" resultType="com.ratel.shiro.entity.Menu">
        SELECT * FROM menu where pid!= 0
    </select>
</mapper>


  1. Controller层(由于是查询操作,并且没有复杂的操作,偷个懒就不写service层)


@RestController
@RequestMapping("mymenu")
public class MenuController {
    @Autowired
    private MenuDao menuDao;
    @RequestMapping("/getMenuTree")
    public List<Menu> getMenuTree(){
        List<Menu> menusBase = menuDao.selectByPid(0);
        List<Menu> menuLNotBase = menuDao.selectAllNotBase();
        for (Menu menu : menusBase) {
            List<Menu> menus = iterateMenus(menuLNotBase, menu.getId());
            menu.setMenuChildren(menus);
        }
        return  menusBase;
    }
    /**
     *多级菜单查询方法
     * @param menuVoList 不包含最高层次菜单的菜单集合
     * @param pid 父类id
     * @return
     */
    public List<Menu> iterateMenus(List<Menu> menuVoList,String pid){
        List<Menu> result = new ArrayList<Menu>();
        for (Menu menu : menuVoList) {
            //获取菜单的id
            String menuid = menu.getId();
            //获取菜单的父id
            String parentid = menu.getPid();
            if(StringUtils.isNotBlank(parentid)){
                if(parentid.equals(pid)){
                    //递归查询当前子菜单的子菜单
                    List<Menu> iterateMenu = iterateMenus(menuVoList,menuid);
                    menu.setMenuChildren(iterateMenu);
                    result.add(menu);
                }
            }
        }
        return result;
    }
}


  1. 启动程序用postman进行测试:



返回的json数据如下:

[
    {
        "id": "1",
        "name": "主菜单1",
        "pid": "0",
        "menuChildren": [
            {
                "id": "4",
                "name": "子菜单1.1",
                "pid": "1",
                "menuChildren": [
                    {
                        "id": "6",
                        "name": "子菜单1.1.1",
                        "pid": "4",
                        "menuChildren": []
                    },
                    {
                        "id": "9",
                        "name": "子菜单1.1.2",
                        "pid": "4",
                        "menuChildren": []
                    }
                ]
            },
            {
                "id": "5",
                "name": "子菜单1.2",
                "pid": "1",
                "menuChildren": []
            }
        ]
    },
    {
        "id": "2",
        "name": "主菜单2",
        "pid": "0",
        "menuChildren": [
            {
                "id": "7",
                "name": "子菜单2.1",
                "pid": "2",
                "menuChildren": []
            },
            {
                "id": "8",
                "name": "子菜单2.2",
                "pid": "2",
                "menuChildren": []
            }
        ]
    },
    {
        "id": "3",
        "name": "主菜单3",
        "pid": "0",
        "menuChildren": []
    }
]

参考链接:

java递归 处理权限管理菜单树或分类

一次性搞定权限树遍历--------权限树后台遍历的通用解决方案

(java后台)用户权限的多级菜单遍历方法

java 用递归实现球上下级(牵涉到对上级的去重)

java递归获取某个父节点下面的所有子节点

java递归算法总结

目录
相关文章
|
2月前
|
Java
java基础(11)函数重载以及函数递归求和
Java支持函数重载,即在同一个类中可以声明多个同名方法,只要它们的参数类型和个数不同。函数重载与修饰符、返回值无关,但与参数的类型、个数、顺序有关。此外,文中还展示了如何使用递归方法`sum`来计算两个数之间的和,递归的终止条件是当第一个参数大于第二个参数时。
31 1
java基础(11)函数重载以及函数递归求和
|
2月前
|
安全 Java 开发者
Java修饰符与封装:理解访问权限、行为控制与数据隐藏的重要性
Java中的修饰符和封装概念是构建健壯、易维护和扩展的Java应用程序的基石。通过合理利用访问权限修饰符和非访问修饰符,开发者能够设计出更加安全、灵活且高效的代码结构。封装不仅是面向对象编程的核心原则之一,也是提高软件项目质量和可维护性的关键策略。
17 1
|
1月前
|
Java
java中的权限修饰符
java中的权限修饰符
|
3月前
|
Java
【Java基础面试三】、说一说你对Java访问权限的了解
这篇文章介绍了Java中的四种访问权限:private、default(无修饰符时的访问权限)、protected和public,以及它们分别在修饰成员变量/方法和类时的不同访问级别和规则。
【Java基础面试三】、说一说你对Java访问权限的了解
|
3月前
|
存储 Java
|
4月前
|
算法 Java
java使用递归及迭代方式实现前序遍历 中序遍历 后序遍历 以及实现层序遍历
java使用递归及迭代方式实现前序遍历 中序遍历 后序遍历 以及实现层序遍历
82 7
|
3月前
|
存储 Java
|
3月前
|
前端开发 Java 编译器
【前端学java】类中的访问权限详解,通俗易懂(5)
【8月更文挑战第9天】类中的访问权限详解,通俗易懂(5)
49 0
|
5月前
|
Java
java实现斐波那契数列(递归、迭代、流)
java实现斐波那契数列(递归、迭代、流)
|
5月前
|
Java 数据安全/隐私保护
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
Java基础手册二(类和对象 对象创建和使用 面向对象封装性 构造方法与参数传递 this关键字 static关键字 继承 多态 方法覆盖 final关键字 访问控制权限修饰符)
34 0
下一篇
无影云桌面