Java树形结构递归

转载自:Java之使用递归查询多级树形结构数据_UnIQUE Eason的博客-CSDN博客_递归查询树形数据

工作中可能会碰到一个表中存在父子关系,需要查询多级结构的树形数据场景(如图1-1),因此我们可以使用递归来实现

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发送请求
在这里插入图片描述
可以看到数据也成功以多级形势返回了
在这里插入图片描述

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值