关键思想:
- 1
最终返回给前台的是一个实体类,该类应该有一个Children字段(实体类属性中具有,或者继承方法来获得,本例子用的是继承)用来接受它的子元素,子元素同样也是该类,也具有Children字段(层层套娃);
- 2
遍历数据库所有元素,将一级元素(父id等于0)直接添加到树中,(其实就是放在该类中),作为基础;
- 3
然后嵌套循环进行再次遍历数据库,进行判断,如果有A元素的父id等于另外一个B元素的id,就将A元素添加到B元素的Children字段中进行储存,也就是将A元素作为B元素的子级;遍历完成之后,每个元素具有的子元素都被安排的明明白白,层级关系建立完成
- 4
这样返回到前台就是这样的数据
建立完成
具体步骤及代码
- 1:数据库建立
数据库元素应该具有自身id,父id来区别层级
- 2:进行数据库元素的全部查询
@Override
public List<GoodsCategoryTree> selectTree(GoodsCategory goodsCategory) {
System.out.println("进入selectTree方法。。。");
List<GoodsCategory> list = this.list(Wrappers.lambdaQuery(goodsCategory));
System.out.println("打印进入gettree的list"+list);
return getTree(this.list(Wrappers.lambdaQuery(goodsCategory)));
}
/**
* 构建树
*
* @param entitys
* @return
*/
private List<GoodsCategoryTree> getTree(List<GoodsCategory> entitys) {
List<GoodsCategoryTree> treeList = entitys.stream()
//过滤(当前id不等于它的父类id)
.filter(entity -> !entity.getId().equals(entity.getParentId()))
//同级类目,根据Sort排序
.sorted(Comparator.comparingInt(GoodsCategory::getSort))
//复制bean
.map(entity -> {
GoodsCategoryTree node = new GoodsCategoryTree();
//转换类型(其实无所谓换不换都一样,两个类基本属性一样)
//转换后,流输出就变成了List<GoodsCategoryTree>
BeanUtil.copyProperties(entity,node);
return node;
}).collect(Collectors.toList());
//将返回的list集合放进树形工具类中,处理后返回
return TreeUtil.build(treeList, CommonConstants.PARENT_ID);
}
①selectTree方法中只是将数据库的数据按照条件查询出来,(一般不限制条件,限制的话也不能限制parentId,限制了会导致树形结构一级没有内容,后面返回到前台的就是空的)并放入getTree方法中;
②getTree方法主要是将查询到的数据转换成流,并进行筛选、排序、转换类等操作,最后以List< GoodsCategoryTree >的形式放入TreeUtil.build()方法中;
③TreeUtil.build()方法是整个树形的核心;通过两层循环,将每个元素都互相做比较,如果是其子类就放进去到它的children字段中,然后将parentId等于0的元素放入需要返回到前台的类中,也就是List< GoodsCategoryTree >类中;返回就是分层的实体类;
代码如下:
/**
* @author
*/
@UtilityClass
public class TreeUtil {
/**
* 两层循环实现建树
*
* @param treeNodes 传入的树节点列表
* @return
*/
//因为改类继承了TreeNode类,所以才会有该类的addChildren方法
public <T extends TreeNode> List<T> build(List<T> treeNodes, Object root) {
//T 类型为 GoodsCategoryTree root = 0
List<T> trees = new ArrayList<>();
//遍历 list中每一个元素为treeNode
for (T treeNode : treeNodes) {
//先把父id等于0的,也就是第一级添加到树结构中
if (root.equals(treeNode.getParentId())) {
//如果元素父类id为0,就添加
trees.add(treeNode);
// trees.sort(Comparator.comparing(TreeNode::getSort));
}
//就再次遍历所有,包括已经添加到树中的
for (T it : treeNodes) {
//遍历所有,如果有元素的父id等于该元素的id,就成为其子类,不管几层,最后都会按层级排序
if (it.getParentId().equals(treeNode.getId())) {
treeNode.addChildren(it);
// treeNode.getChildren().sort(Comparator.comparing(TreeNode::getSort));
}
}
}
return trees;
}
其中泛型T是方法传进来的,这里也就是List< GoodsCategoryTree >类,看一下实体类:
/**
* 商品类目
*
* @author JL
* @date 2019-08-12 11:46:28
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class GoodsCategoryTree extends TreeNode {
/**
* 所属租户
*/
@ApiModelProperty(value = "所属租户")
private String tenantId;
/**
* (1:开启;0:关闭)
*/
@ApiModelProperty(value = "1:开启;0:关闭")
private String enable;
/**
* 父分类编号
*/
@ApiModelProperty(value = "父分类编号")
private String parentId;
/**
* 名称
*/
@ApiModelProperty(value = "名称")
private String name;
/**
* 描述
*/
@ApiModelProperty(value = "描述")
private String description;
/**
* 图片
*/
@ApiModelProperty(value = "图片")
private String picUrl;
/**
* 排序
*/
@ApiModelProperty(value = "排序")
private Integer sort;
/**
* 创建时间
*/
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
/**
* 最后更新时间
*/
@ApiModelProperty(value = "最后更新时间")
private LocalDateTime updateTime;
/**
* 逻辑删除标记(0:显示;1:隐藏)
*/
@ApiModelProperty(value = "逻辑删除标记")
private String delFlag;
/**
* 跳转页面
*/
@ApiModelProperty(value = "跳转页面")
private String page;
}
并没有children字段,但是需要用到这个字段来进行子元素的存储,前文也提到,要么建字段,要么继承有这个字段的类,这里选择的继承类;
//因为改类继承了TreeNode类,所以才会有该类的addChildren方法
public <T extends TreeNode> List<T> build(List<T> treeNodes, Object root)
继承的这个类
package com.uk.mall.cloud.common.core.entity;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* @author
*/
@Data
public class TreeNode {
//在本方法中需要用到的属性写上
protected String id;
protected String parentId;
private Integer sort;
protected List<TreeNode> children = new ArrayList<>();
public void addChildren(TreeNode treeNode) {
children.add(treeNode);
}
public List<TreeNode> getChildren() {
if(children.size()<=0){
return null;
}
return children;
}
}
继承的类里面需要写上在这个方法中需要用到的属性,因为在该类中暂时类型还是泛型T,所以无法通过.get的方法来获取参数属性,继承类可以解决;
- 3:分层完成,返回前端页面即可;
需要多加分层只需要添加相应的元素,将父id指向已存在的id就行