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