转载自:Java之使用递归查询多级树形结构数据_UnIQUE Eason的博客-CSDN博客_递归查询树形数据
工作中可能会碰到一个表中存在父子关系,需要查询多级结构的树形数据场景(如图1-1),因此我们可以使用递归来实现
首先我建了一个测试的菜单表:
其中最顶级的菜单的父类ID是用0表示的,下面我们就来查询这张表
代码演示
建一个返回菜单数据的实体类
public class Menu {
/** 主键id */
private long ID;
/** 父类主键 */
private long parentid;
/** 菜单名称 */
private String name;
/** 菜单类型 */
private String type;
/** 菜单编码 */
private String code;
/** 子类菜单数据 */
@TableField(exist = false)
private List<Menu> childMenu;
}
这里省略了get,set方法,childMenu是用于装子类数据的;@TableField(exist = false)
表示该属性不为数据库表字段,但是必须使用
控制层代码:
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
public TestService testService;
@GetMapping("/menu")
public List<Menu> findMenu(@RequestParam long parentID) {
return testService.findMenu(parentID);
}
}
业务实现层代码:
public interface TestService {
/**
* 获取菜单多级信息
* @param parentID 父类主键
* @return List<Menu>
*/
List<Menu> findMenu(long parentID);
}
@Service
public class TestServiceImpl implements TestService {
@Autowired
private BaseMapper<Menu> baseMapper;
@Override
public List<Menu> findMenu(long parentID) {
QueryWrapper queryWrapper = new QueryWrapper<>();
//查询条件
queryWrapper.eq("parentid", parentID);
List<Menu> list = baseMapper.selectList(queryWrapper);
for (Menu menu : list) {
//递归子类数据
menu.setChildMenu(findMenu(menu.getID()));
}
return list;
}
}
先查询指定父类主键的数据,然后在循环中再次调用此方法查询此次数据是否存在子类数据;我这里持久层使用的是mybatisPlus,如果没引入需要另写sql
sql语句:
select id,`name`,`type`,`code`,parentID from menu where parentID = ?
测试
使用postman发送请求,参数为0,表示我要查询所有顶级菜单的数据
可以看到数据是以多级形势返回成功
[
{
"name": "商品一级菜单",
"type": "商品",
"code": "0001",
"childMenu": [
{
"name": "商品二级菜单",
"type": "商品",
"code": "1001",
"childMenu": [
{
"name": "商品三级菜单",
"type": "商品",
"code": "1101",
"childMenu": [],
"id": 6,
"parentID": 3
}
],
"id": 3,
"parentID": 1
},
{
"name": "商品二级菜单(2)",
"type": "商品",
"code": "1002",
"childMenu": [],
"id": 4,
"parentID": 1
}
],
"id": 1,
"parentID": 0
},
{
"name": "店铺一级菜单",
"type": "店铺",
"code": "0002",
"childMenu": [
{
"name": "店铺二级菜单",
"type": "店铺",
"code": "2002",
"childMenu": [],
"id": 5,
"parentID": 2
}
],
"id": 2,
"parentID": 0
}
]
循环访问数据库并不推荐,于是做了一些优化
上一篇文章里说到使用递归查询多级树形结构数据,后来我想了一下,对于一直循环访问数据库还是不推荐,于是这篇文章对递归查询做了一些优化;既然不要循环访问数据库,那么我们就可以一次把所有数据查出来存到List集合里,再通过递归List把数据存到另一个List也是一个道理,下面上代码
首先依然建了一个测试菜单表:
实体类:
public class Menu {
/** 主键id */
private long ID;
/** 父类主键 */
private long parentid;
/** 菜单名称 */
private String name;
/** 菜单类型 */
private String type;
/** 菜单编码 */
private String code;
/** 子类菜单数据 */
@TableField(exist = false)
private List<Menu> childMenu;
get set方法省略...
}
childMenu是用于装子类数据的;@TableField(exist = false)
表示该属性不为数据库表字段,但是必须使用
控制层代码:
@RestController
@RequestMapping("/test")
public class TestController {
@Autowired
public TestService testService;
@GetMapping("/menu")
public List<Menu> findMenu() {
return testService.findMenu();
}
}
这里我们就不需要传父类主键做参数了
业务实现层代码:
public interface TestService {
/**
* 获取菜单多级信息
* @return List<Menu>
*/
List<Menu> findMenu();
}
业务层逻辑有所改动,先将所有数据存到List,再递归遍历到另一个List中返回
@Service
public class TestServiceImpl implements TestService {
@Autowired
private BaseMapper<Menu> baseMapper;
@Override
public List<Menu> findMenu() {
//数据查询
List<Menu> list = baseMapper.selectList(new QueryWrapper());
//新建一个用于接收数据的list
List<Menu> resultList = new ArrayList<>();
for (Menu result : list) {
if (result.getParentID() == 0) {
//调用方法给子类添加数据
resultList.add(getMenuTree(result, list));
}
}
return resultList;
}
private Menu getMenuTree(Menu result, List<Menu> list) {
for (Menu menu : list) {
//如果父类主键等于传过来实体类的ID
if (menu.getParentID() == result.getID()) {
if (result.getChildMenu() == null) {
result.setChildMenu(new ArrayList<>());
}
// 递归调用
result.getChildMenu().add(getMenuTree(menu, list));
}
}
return result;
}
}
sql语句
select id,`name`,`type`,`code`,parentID from menu where parentID = ?
测试一下
使用postman发送请求
可以看到数据也成功以多级形势返回了