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

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

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, collect1));
                    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)){
    
    
            wrapper.eq(TreeEntity::getId,node);
        }else{
    
    
        //否则返回所有父节点id为传入根节点id的下级信息
            wrapper.eq(TreeEntity::getParentId,node);
        }
        List<TreeEntity> list = list(wrapper);
        return list;
   	}

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

猜你喜欢

转载自blog.csdn.net/m0_49605579/article/details/131415933