关于树形结构查询的接口设计

在实际开发中,经常会遇到树形结构的查询,如:菜单树、公司组织机构树、地区区划树等等业务,这里写下两个接口设计方案以供日后使用。

1.表结构设计

//一般单表树形结构基本上都为以下结构:
id,name,parentId,其他业务指标
//多表树形同理,设计思路都是通过parentId关联在一起

2.树形返回类设计

@Data
public class TreeNodeNew implements Serializable {

    private String id;
	//名称
    private String name;
	//上级id
    private String parentId;
	//子节点
    private List<TreeNodeNew> childrenTreeNode;
    //其他业务字段...
}

3.树形方案一:递归获取全部父子节点(若数据量太大则不推荐用此方法)

	//传入需查询的根节点
    public List<TreeNodeNew> findTree(String node) {
    	if(StrUtil.isEmpty(node)){
            node = 当前登录人所绑定的node节点
        }
        //LambdaQueryWrapper<TreeEntity>wrapper = new LambdaQueryWrapper<>();
        //wrapper.likeRight(TreeEntity::getId,node);
        //查出全部数据,这里可以优化:想办法只查询出需要的所有数据,比如51-5101-510101这种格式的结构like51就行
        List<TreeEntity> treeList = list(wrapper);
        //转为返回树形集合对象
        List<TreeNodeNew> trees = treeList.stream().map(op -> {
            TreeNodeNew tree = new TreeNodeNew();
            tree.setId(op.getCode());
            tree.setParentId(op.getParentId());
            tree.setName(op.getName());
            return tree;
        }).collect(Collectors.toList());
        //用传入的根节点做第一级,调用getChildren设置所有下属节点
        List<TreeNodeNew> collect = trees .stream()
                .filter(item -> node.equals(item.getId()))
                .map(item -> {
                    item.setChildrenTreeNode(getChildren(item, trees));
                    return item;
                }).sorted(Comparator.comparing(TreeNodeNew::getId)).
                collect(Collectors.toList());

         return collect;
	}
	//传入父节点和所有数据节点信息集合,先查出所有父节点id为传入父节点id的下属数据,再递归set下属数据的childrens
    private static List<TreeNodeNew> getChildren(TreeNodeNew treeEntity, List<TreeNodeNew> treeEntityList) {
        List<TreeNodeNew> collect = treeEntityList.stream()
                .filter(item -> treeEntity.getId().equals(item.getParentId()))
                .map(item -> {
                    item.setChildrenTreeNode(getChildren(item, treeEntityList));
                    return item;
                }).sorted(Comparator.comparing(TreeNodeNew::getId))
                .collect(Collectors.toList());
        return collect;
    }

4.树形方案二:懒加载

//懒加载实现:每次传入一个根节点,只需要返回所有parentId为传入根节点的数据集合即可,前端每次拿到数据
//显示在根节点下面,点一次不同根节点请求一次,渲染一次
    public List<TreeEntity> findTree(String node) {
        LambdaQueryWrapper<TreeEntity> wrapper = new LambdaQueryWrapper<>();
        //当传入根节点为空时,为第一次进入,默认返回该根节点信息
        if(StrUtil.isEmpty(node)){
        	node = "";//根节点id
            wrapper.eq(TreeEntity::getId,node);
        }else{
        //否则返回所有父节点id为传入根节点id的下级信息
            wrapper.eq(TreeEntity::getParentId,node);
        }
        List<TreeEntity> list = list(wrapper);
        return list;
   	}

5.总结:用方案一递归返回,优点是前端可以一次性拿到所有数据节点信息,相比懒加载不用点一次下级请求一次,只是第一次请求时效率慢点,可以考虑加缓存,但也要维护缓存。缺点是在数据量特别大的时候效率相当慢,如全国行政区划这种就适合用方案二懒加载形式,以时间换空间,方案一则相反。方案二优点是接口设计简单,维护容易。缺点是需要频繁请求数据来渲染页面,且需要回显时会比较麻烦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值