关于树结构的查询优化,及权限树的查询优化

在有限的经验下,主要讨论以下两部分内容

1.对树形数据结构的查询,且层级较少,采用添加分级字段的方式优化
2.针对前端使用ztree ,list直接返回结果集,存在授权问题的查询,采用添加层次码的方案

1.针对需要树结构的查询
  • 在有限的多级目录情况下,可以添加分级字段,(1级目录,2级目录,3级目录),
  • 1.在这个基础上假设只有三级目录,那么我们可以直接分3次查询,此时得到了3个关于1、 2、 3层级对应的list;
  • 2.上一步的操作,使我们极大的改善了数据库查询的效率,只需要3次查询即可拿到所有的数据,但同样,需要解决拼树的问题
  • 3.对树的拼接,在目前大部分服务器使用多核的情况下,推荐使用java8 Stream操作对原始list进行加工。

基于以上假设,单条数据如下,即正常查询list中数据泛型为Regions

		@Data
		public class Regions {
		    private Integer objectId;//id
		    private String regionName;//区域名称
		    private String regionCode;//区域编码
		    private Integer level;//区域级别
		    private String parentCode;//上级区域编号
		}

返回到页面的对象

@Data
	public class TreeNode {
	    private String regionName;
	    private String regionCode;
	    private String parentCode;
	    List<TreeNode> children;
	}

算法写法

import javax.annotation.Resource;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

@Service
public class RegionServiceImpl implements RegionService {
    @Resource
    private RegionsMapper regionsMapper;

    @Override
    public List<TreeNode> getRegionTree() {
    	//根据层级关系查询了3次
        List<Regions> level1 = regionsMapper.getListByLevel(1);
        List<Regions> level2 = regionsMapper.getListByLevel(2);
        List<Regions> level3 = regionsMapper.getListByLevel(3);
        //先将最末级转为TreeNode 的形式
        List<TreeNode> node3 = level3.stream().map(l3 -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setRegionCode(l3.getRegionCode());
            treeNode.setRegionName(l3.getRegionName());
            treeNode.setParentCode(l3.getParentCode());
            //没有下级children 不做处理
            return treeNode;
        }).collect(toList());

        //对二级目录进行抽象
        List<TreeNode> node2 = level2.stream().map(a -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setRegionName(a.getRegionName());
            treeNode.setRegionCode(a.getRegionCode());
            treeNode.setParentCode(a.getParentCode());
            treeNode.setChildren(node3.stream().filter(l3 -> l3.getParentCode().equals(a.getRegionCode())).collect(toList()));
            return treeNode;
        }).collect(toList());
        //对一级目录继续抽象并返回
        List<TreeNode> node1 = level1.stream().map(a -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setRegionName(a.getRegionName());
            treeNode.setRegionCode(a.getRegionCode());
            treeNode.setParentCode(a.getParentCode());
            treeNode.setChildren(node2.stream().filter(l2 -> l2.getParentCode().equals(a.getRegionCode())).collect(toList()));
            return treeNode;
        }).collect(toList());
        return node1;
    }
}

在这里插入图片描述
1000条数据,耗时1s多一点,这里作者连的是远程数据库加上网速也不是很乐观,若网络连接畅通,还会快一些。

备注 :若层级较多时,可以进一步简化(抽象),以for循环的形式读取每个层级的数据,然后以同样以for循环的形式对数据进行从低到高的封装,一个方法做循环层级读取数据,另一个方法做对多个层级进行 树的推导
2.针对zTree授权树的查询优化
  • 前言:
  • 前端使用zTree插件时,可以进一步简化后端的查询,也不存在树的结构化返回,实际上,如果没有权限的管理,我们只需要拿到所有的数据,并对数据字段进行取别名,使其符合zTree的规范即可。

  • 当目录层级存在权限时,比如子目录存在权限,但父目录并没有授权,但在前端界面中,我们同样需要加载父级目录显示给用户,以维持整个目录的结构。

  • 思考

    理论上,我们从上往下进行递归,每验证一个目录需要验证 它的子目录 是否存在权限,如果存在权限,那么就将这个目录存放到list中,并继续递归它的下级目录
    弊端 :当数据量较大时,其需要不断查询下级目录,并不断做验证权限的查询,验证 本级及下级是否存在权限 理论上也需要做递归,这样就导致了大量的查询操作,使效率极大的被限制。

  • 解决方案

    加入层次码(同样是不可重复的)字段,即 比如上级菜单的层次码为 xxx ,那么下级菜单的层次码 必定为xxxyyy 或xxxzzz,并且它的多层子目录的层次码必定为xxx开头。
    基于以上的假设,我们的思路如下:

    • 1.查询到所有的已授权目录的层次码,并标识为这样的一个List 集合:List<Authority> authList Authority 主要字段为 ccm(层次码)

    • 2.查询所有的目录,即不管是否存在授权都进行查询 ,存放于:List<Menu> menus ,Menu的主要字段是业务字段,及ccm 字段,注意menus 是可以直接返回到前端的一个list,我们现在要做的只是筛选授权的数据,并返回即可

    • 3.使用java 8 Stream 方法,根据层次码进行筛选,代码如下:

    public List<Menus> getAuthroityMenus(){
        List<Menus> menus = new ArrayList<>();//假设这里查询了数据库,获得了所有的目录
        List<Authority> authList = new ArrayList<>();//假设这里查询了数据库,得到了所有授权的目录
        
       return menus.stream().filter(a->{
            //a,表示每一个menu对象
            //同样的,下方的b 对应每一个Authority对象
            //遍历授权目录,如果 授权目录的ccm   以此刻的Menu.ccm 开头,则表示这个menu是已授权的上级,
            if (authList.stream().filter(b->b.getCcm().startsWith(a.getCcm())).count()>0){
                return true;
            }
            return false;
        }).collect(Collectors.toList());
        
    }

java8 的写法有不了解的可以搜下相关资料,博主自己也写过一些java 8相关的内容,这里就不做具体的细节介绍了。
有疑问或有其他更好的方法,也希望大家在评论区积极发言!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

灵湖映北辰

年轻人,要讲武德!!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值