这是我参与11月更文挑战的第17天,活动详情查看:2021最后一次更文挑战
完整示例代码地址如下: github.com/Dr-Water/sp…
一、 权限树的问题由来
- 在开发中难免遇到一个有多级菜单结构树,或者多级部门的结构树,亦或是省市区县的多级结构,数据结构类似如下的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程序中进行树状结构的构建
- 方法二: 第一次将最高层次的数据查询出来,然后多次循环查询数据库将子数据查询出来
由于博主的前端水平有限,目前只能用后端的实现方式,再加上每次查询数据库的开销比较大,所以本文使用方案二的方法一进行验证
实现步骤
以菜单的结构树为例
- 准备mysql数据库的基础数据
2. java的实体类:
@Data
@NoArgsConstructor
public class Menu implements Serializable {
private String id;
private String name;
private String pid;
private List<Menu> menuChildren;
}
复制代码
- java的dao层
@Mapper
public interface MenuDao {
/**
* 根据父类id查询子类菜单
* @param pid
* @return
*/
List<Menu> selectByPid(Integer pid);
/**
* 查询所有的菜单
* @return
*/
List<Menu> selectAll();
/**
* 查询除了一级菜单以外的菜单
* @return
*/
List<Menu> selectAllNotBase();
}
复制代码
- 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>
复制代码
- 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;
}
}
复制代码
- 启动程序用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递归算法总结