@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<>();
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;
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
复制代码