一、起因
之前在技术群里发现了有人问类似于树状关系要怎么查询比较好(具体如下)
当时在牛客上也发现有人询问了这个问题。当时考虑到了迭代遍历获取子节点加入,然后下面有人提到了递归于是便想着第二天来实现一下、在中午跟家人聚完餐之后就来进行了实现。
二、实现
自己的表:
springboot准备:这里使用的mybatis-plus来进行查询(lambdaquery是真的好用)
1、统一返回类
/**
统一返回类
*/
@Data
@ApiModel("result")
public class ResultBean<T> implements Serializable {
@ApiModelProperty("状态码")
private int code;
@ApiModelProperty("返回消息")
private String message;
@ApiModelProperty("返回数据")
private T data;
/**
* 返回结果
* */
public static<T> ResultBean<T> build(int code , String msg , T data){
return new ResultBean<>(code,msg , data);
}
public static<T> ResultBean<T> ok(T data){
return new ResultBean<>(data);
}
//·····一下省略
2、实体类(实际开发需要DTO、vo层的这里我这样写肯定是不规范的)
@Data
@TableName("sql_test_practise")
public class TreeSQL {
/**
*逐渐id
* */
private Long id;
/**
* 父节点id
* */
private Long pid;
/**
* 内容描述
* */
private String content;
/**
* 子节点
* */
@TableField(exist = false)
private List<TreeSQL> childNode;
}
3、service层
@Service
public class ITreeSQLService extends ServiceImpl<TreeSQLMapper, TreeSQL> implements TreeSQLService {
}
4、进行测试
1、迭代方式
@GetMapping("/test/1")
public ResultBean<List<TreeSQL>> hello(HttpServletRequest request , HttpServletResponse response){
List<TreeSQL> treeSQLList = treeSQLService.lambdaQuery().eq(TreeSQL::getPid,"0").list();
for (TreeSQL treeSQL : treeSQLList){
List<TreeSQL> childTreeList = treeSQLService.lambdaQuery().eq(TreeSQL::getPid,treeSQL.getId()).list();
treeSQL.setChildNode(childTreeList);
for (TreeSQL child : childTreeList) {
List<TreeSQL> grandChildList = treeSQLService.lambdaQuery().eq(TreeSQL::getPid,child.getId()).list();
child.setChildNode(grandChildList);
}
}
return ResultBean.ok(treeSQLList);
}
2、递归方式
@GetMapping("/test/2")
public ResultBean<List<TreeSQL>> test2(){
//获取数据库中所有的数据
List<TreeSQL> allList = treeSQLService.list();
/**
* 1、在所有的数据中过滤获取一级菜单的数据
* 2、map中通过递归函数获取一级节点中的子节点列表
* */
List<TreeSQL> treeList = allList.stream().filter(parentNode -> parentNode.getPid()==0L)
.map(node -> getChild(node, allList)).toList();
return ResultBean.ok(treeList);
}
/**
* 递归辅助函数
* @param sql 上级节点
* @param allList 所有数据
* */
public TreeSQL getChild(TreeSQL sql , List<TreeSQL> allList){
/**
* 1、filter:获取子节点pid是否等于id
* 2、map:递归获取子节点
* */
List<TreeSQL> treeSQL = allList.stream()
.filter(subNode -> subNode.getPid().equals(sql.getId()))
.map(subNode -> getChild(subNode, allList)).toList();
sql.setChildNode(treeSQL);
return sql;
}
三、比对
1、性能
这里是加了一个过滤器来进行接口性能测试
迭代:
递归:
可以看到性能提升来快5倍
2、结果
在测试的结果中几乎是完全一样的。但是!!
如果在最后一层节点下在新增一个节点的话例如
当前节点有四层,迭代查询的结果由于是两层迭代的原因,所以只能查询到西瓜着一层。
迭代结果:
递归结果:
这里加入业务变化。原本最大是三级结构、后因为需求变成了四级结构,迭代需要继续加一层for、而递归的代码不需要任何的变化。
代码能够适应业务的变化,可以说耦合度完完全全的降低了。