在实际开发中,经常会遇到树形结构的查询,如:菜单树、公司组织机构树、地区区划树等等业务,这里写下两个接口设计方案以供日后使用。
1.表结构设计
id,name,parentId,其他业务指标
2.树形返回类设计
@Data
public class TreeNodeNew implements Serializable {
private String id;
private String name;
private String parentId;
private List<TreeNodeNew> childrenTreeNode;
}
3.树形方案一:递归获取全部父子节点
public List<TreeNodeNew> findTree(String node) {
if(StrUtil.isEmpty(node)){
node = 当前登录人所绑定的node节点
}
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());
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;
}
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.树形方案二:懒加载
public List<TreeEntity> findTree(String node) {
LambdaQueryWrapper<TreeEntity> wrapper = new LambdaQueryWrapper<>();
if(StrUtil.isEmpty(node)){
wrapper.eq(TreeEntity::getId,node);
}else{
wrapper.eq(TreeEntity::getParentId,node);
}
List<TreeEntity> list = list(wrapper);
return list;
}
5.总结:用方案一递归返回,优点是前端可以一次性拿到所有数据节点信息,相比懒加载不用点一次下级请求一次,只是第一次请求时效率慢点,可以考虑加缓存,但也要维护缓存。缺点是在数据量特别大的时候效率相当慢,如全国行政区划这种就适合用方案二懒加载形式,以时间换空间,方案一则相反。方案二优点是接口设计简单,维护容易。缺点是需要频繁请求数据来渲染页面。