项目中免不了会遇到树形结构的查询,今天分享一下我写的一个查询方法。 先看数据库的数据,主要有id,parentId,name三个字段(可能还有其他本次无用的很多字段),根节点的父id设置为0
实现的效果
[{
"children": [{
"children": [{
"children": [{
"children": [],
"id": 11,
"name": "四级目录1111",
"parentId": 5
}],
"id": 5,
"name": "三级目录111",
"parentId": 3
}, {
"children": [],
"id": 6,
"name": "三级目录112",
"parentId": 3
}, {
"children": [],
"id": 7,
"name": "三级目录113",
"parentId": 3
}],
"id": 3,
"name": "二级目录11",
"parentId": 1
}],
"id": 1,
"name": "一级目录1",
"parentId": 0
}, {
"children": [{
"children": [{
"children": [{
"children": [],
"id": 12,
"name": "四级目录2111",
"parentId": 8
}],
"id": 8,
"name": "三级目录211",
"parentId": 4
}, {
"children": [],
"id": 9,
"name": "三级目录212",
"parentId": 4
}, {
"children": [],
"id": 10,
"name": "三级目录213",
"parentId": 4
}],
"id": 4,
"name": "二级目录21",
"parentId": 2
}],
"id": 2,
"name": "一级目录2",
"parentId": 0
}]
下面是业务层的实现代码,主要是递归的思想
package yulisao.test;
import com.alibaba.fastjson.JSON;
import yulisao.bean.Menu;
import yulisao.bean.MenuTree;
import java.util.ArrayList;
import java.util.List;
public class MenuTreeTest {
public static void main(String[] args) {
List resp = getMenuTreeList(0L); // c从根节点开始查找。根节点的id是0
System.out.println(JSON.toJSONString(resp));
}
public static List getMenuTreeList(Long parentId) {
List menuTrees = new ArrayList<>();
//List
实体和返回对象
package yulisao.bean;
public class Menu {
private Long id;
private Long parentId;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package yulisao.bean;
import java.util.List;
public class MenuTree {
private Long id;
private Long parentId;
private String name;
List children;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getParentId() {
return parentId;
}
public void setParentId(Long parentId) {
this.parentId = parentId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getChildren() {
return children;
}
public void setChildren(List children) {
this.children = children;
}
}
创建表语句
CREATE TABLE `t_menu` (
`id` INT(11) NOT NULL,
`parentId` INT(11) NOT NULL,
`url` VARCHAr(50) NOT NULL,
`menuname` VARCHAr(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=gbk COMMENT '菜单表';
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('1','0','yourUrl','一级目录1');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('2','0','yourUrl','一级目录2');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('3','1','yourUrl','二级目录11');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('4','2','yourUrl','二级目录21');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('5','3','yourUrl','三级目录111');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('6','3','yourUrl','三级目录112');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('7','3','yourUrl','三级目录113');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('8','4','yourUrl','三级目录211');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('9','4','yourUrl','三级目录212');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('10','4','yourUrl','三级目录213');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('11','5','yourUrl','四级目录1111');
INSERT INTO `t_menu` (`id`, `parentId`, `url`, `menuname`) VALUES('12','8','yourUrl','四级目录2111');
分析一下getMenuTreeList方法,先查出父id为0的全部数据(即一级菜单),遍历一级菜单得到一级菜单的id后,在根据这个id去查二级菜单的数据,一层一层往下查。查不到的时候menuList为空也就不会进入for循环了自然不会无限循环。另外一种写法和这个类似,就是不带任何条件先查询出所有的数据,然后从所有数据中去判断他们的父id是不是我要找的,若是则添加。
public static ListgetMenuTreeList2(Long parentId,List allMenuList) { List menuTrees = new ArrayList<>(); for (Menu menu : allMenuList) { //遍历所有数据,如果该数据的父id等于当前要找的id就添加 if (parentId.equals(menu.getParentId())) { MenuTree nodeTree = new MenuTree(); nodeTree.setId(menu.getId()); nodeTree.setParentId(menu.getParentId()); nodeTree.setName(menu.getName()); nodeTree.setChildren(getMenuTreeList2(menu.getId(), allMenuList)); // 递归 menuTrees.add(nodeTree); } } return menuTrees; }
二者的区别就是只查一次数据库和多次的区别,具体选哪个视菜单表的数据量有多少以及他的层级有多深。数据量不大层级不深,推荐用后者,一次查询把数据全部加载到内存中来,减少数据库连接的开销。



