一、起因
之前在技术群里发现了有人问类似于树状关系要怎么查询比较好(具体如下)
当时在牛客上也发现有人询问了这个问题。当时考虑到了迭代遍历获取子节点加入,然后下面有人提到了递归于是便想着第二天来实现一下、在中午跟家人聚完餐之后就来进行了实现。
二、实现
自己的表:
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、而递归的代码不需要任何的变化。
代码能够适应业务的变化,可以说耦合度完完全全的降低了。