在有限的经验下,主要讨论以下两部分内容
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相关的内容,这里就不做具体的细节介绍了。
有疑问或有其他更好的方法,也希望大家在评论区积极发言!