MP - 加载数据库中树型数据(同步树)
- 数据表结构
- 使用场景
- 分析与实现
一:数据表结构
-
Department
- id
- pid
二:使用场景
在前端页面的分类树中:点击树节点,得到当前节点的所有子节点(树结构),展开分类树。
三:分析与实现
-
环境
- Mybatis-Plus 3.4.3
- Java 8
-
分析
1> 由于实体对象中的部分字段与前端用于显示的数据字段名称不同 + 作为树节点应该作为树节点(
遵循一种规范/接口
),所以应该单独创建实体VO
类用于与前端数据的交互;@Data @AllArgsConstructor @NoArgsConstructor public class DepartmentTreeVO extends Department implements INode { // 展示内容 private String label; // 子节点 private List<? extends INode> children; // 标记是否有子节点 private Boolean hasChildren; @Override public String getParentId() { return this.getPid(); } }
2> VO应该继承原有实体,并且实现INode树节点规范;
public interface INode extends Serializable { String getId(); String getParentId(); List<? extends INode> getChildren(); default Boolean getHasChildren() { return this.getChildren() != null && !this.getChildren().isEmpty();}; }
3> 为了方便VO与实体之间数据的拷贝,需要使用BeanUtils工具类完成Bean对象之间属性复制;
import org.springframework.beans.BeanUtils; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; public class XBeanUtil { /** * 复制bean的属性(使用到了spring框架中的BeanUtils) * * @param source 源 要复制的对象 * @param target 目标 复制到此对象 */ public static void copyProperties(Object source, Object target) { BeanUtils.copyProperties(source, target); } /** * 复制对象 * * @param source 源 要复制的对象 * @param target 目标 复制到此对象 * @param <T> * @return */ public static <T> T copy(Object source, Class<T> target) { if(source == null || target == null){ return null; } try { T newInstance = target.newInstance(); BeanUtils.copyProperties(source, newInstance); return newInstance; } catch (Exception e) { throw new RuntimeException(e); } } /** * 复制list * * @param source * @param target * @param <T> * @param <K> * @return */ public static <T, K> List<K> copyList(List<T> source, Class<K> target) { if (null == source || source.isEmpty()) { return Collections.emptyList(); } return source.stream().map(e -> copy(e, target)).collect(Collectors.toList()); } }
4> 明确输入与输出
- 输入:根节点id,这个根节点一般被定义为
id=-1
的虚拟节点- 数据库中pid=-1的记录都会被挂在最外层
虚拟根节点
上; - 虚拟根节点在数据库中并不存在,应该在形成树结构时(
页面初始化时
)手动初始化该节点,同时将其id置为-1并将下方节点挂在当前节点下面。
- 数据库中pid=-1的记录都会被挂在最外层
- 输出:List
- 输入:根节点id,这个根节点一般被定义为
-
实现
1> 调用
List<DepartmentTreeVO> result = buildTreeList("-1", departmentTreeVOList);
2> 根据一个id获取其所有子节点:getChildNodes
public List<DepartmentTreeVO> getChildNodes(String pid, List<DepartmentTreeVO> departments) { return departments.stream().filter(x -> pid.equals(x.getParentId())).collect(Collectors.toList()); }
3> 递归生成树结构
/** * 构建树型数据集合 * * @param pid 父节点pid(注:当前节点不会作为结果根节点) * @param list 所有节点数据(思想:从数据库中拿到所有数据,逻辑只做结构处理) * @return */ public List<DepartmentTreeVO> buildTreeList(String pid, List<DepartmentTreeVO> list) { List<DepartmentTreeVO> childList = getChildNodes(pid, list); List<DepartmentTreeVO> departmentTreeVOList = new ArrayList<>(); for (DepartmentTreeVO node : childList) { List<DepartmentTreeVO> children = buildTreeList(node.getId(), list); if (!children.isEmpty()) { node.setLabel(node.getDeptName()); node.setHasChildren(true); node.setChildren(children); } else { node.setHasChildren(false); } departmentTreeVOList.add(node); } return departmentTreeVOList; }