0


树形结构的表设计与Java接口实现

文章目录

1、开发需求中的树形结构

树形结构在日常开发中很常见,如:

在这里插入图片描述

再比如:

在这里插入图片描述

还有:

在这里插入图片描述

2、表结构设计

这种树形结构,其 **

核心字段为parentId

** ,即父节点id。先看上面课程信息树形结构的表设计:

在这里插入图片描述

精髓就是:每条数据,id是它自己的名字,parentId是它爹的名字。根据parentId能知道它爹是谁,而

它的id就是它儿子的parentId

,WHERE parentId = Id就知道了它的儿子是谁。

MySQL表结构设计如下:
字段名数据类型解释是否必须idvarchar主键,唯一标识这条数据,

也是它子节点的parentId

是namevarchar分类中文名称,以后要展示在前端的信息是parentIdvarchar

核心字段,父节点id,它爹的名字

是is_showtinyint控制这条数据是否显示,就是逻辑删除的那个味儿根据需求分析是否加,像全国省市区,自然不用orderbyint排序字段,同层的节点,返回时谁先谁后看需求,对节点展示有顺序要求时用is_leaftinyint是否叶子节点,1是0否看需求,方便后端的一个字段nodelevelint层级标识字段看需求,像省市区树形,这个字段可以标识下1省级2市级3区级

3、接口实现代码

3.1 模型类与接口定义

定义返回给前端的vo类,核心属性

childrenTreeNodes
//伪代码//假设树形结构表的PO类叫TreePo@DatapublicclassTreeVoextendsTreePo{List<TreeVo> childrenTreeNodes;}//注意这里childrenTreeNodes类型为TreeVo,而不是TreePo,因为你儿子也有自己的儿子

随便定义个示意接口,免得突兀:

@Slf4j@RestControllerpublicclassTreeInfocontroller{@GetMapping("/tree/info/list")publicList<TreeVo>queryTreeNodes(){returnnull;}}

3.2 Mapper层开发

这里以上面的行业分类树形表为例展示如何进行mapper层SQL的书写:

在这里插入图片描述

当树形结构的

层级固定,比如都只有两级,此时使用表的自联结查询即可完成

# 假设表名是t_tree# pid即核心字段parentIdSELECT
     one.id one_id,
     one.name one_name,
     one.pid one_pid,
     two.id two_id,
     two.name two_name,
     two.pid two_pid
FROM t_tree one
INNERJOIN t_tree two
ON one.id = two.pid
WHERE one.pid='根节点id';# 有一条无意义数据,叫根节点,其子节点就是前端页面的第一级数据# 有排序字段的话继续order by one.orderbyField,two.orderbyField

在这里插入图片描述

这就查出来了前两级:

在这里插入图片描述

当树形结构层级不固定

,有的两级深、有的三级深,则应

MySQL递归查询
WITH RECURSIVE tem_table AS(SELECT*FROM  t_tree one WHERE id='0'UNIONALLSELECT two.*FROM t_tree two INNERJOIN tem_table ON tem_table.id = two.pid
)SELECT*FROM  tem_table ;# 有排序需求时后面继续order by tem_table.id......
- tem_table是一个表名
- 使用UNIONALL 不断将每次递归得到的数据加入到表tem_table中
-select*from t_tree p where id='0’即tem_table表中的初始数据是id=0的记录,即根节点
- 通过inner join tem_table ON tem_table.id = two.pid 找到id='0’的下级节点
- 最后select*from tem_table拿递归得到的所有数据

以上是MySQL8.0的写法,再补一个MySQL5.7版本的递归写法

//传入id,返回这个id所在节点的所有子节点delimiter $$ 
dropfunctionifexists get_child_list$$ 
createfunction get_child_list(in_id varchar(10))returnsvarchar(1000)begindeclare ids varchar(1000)default'';declare tempids varchar(1000);set tempids = in_id;while tempids isnotnulldoset ids = CONCAT_WS(',',ids,tempids);select GROUP_CONCAT(id)into tempids from t_tree where FIND_IN_SET(pid,tempids)>0;endwhile;return ids;end  
$$ 
delimiter;

两个版本的SQL递归实现参考这篇文章:https://blog.csdn.net/llg___/article/details/130908373

定义mapper接口:

publicinterfaceTreeMapperextendsBaseMapper<TreePo>{//这里返回vo类型,方便后面代码。不过是查询出来的字段没有vo中的childrenTreeNodes,那就为nullpublicList<TreeVo>selectTreeNodes(String id);}

mapper.xml文件

<selectid="selectTreeNodes"resultType="com.content.model.dto.TreeVo"parameterType="string">
    with recursive t1 as (
        select * from  t_tree p where  id= #{id}
        union all
        select t.* from t_tree t inner join t1 on t1.id = t.pid
    )
    select *  from t1 
</select>

3.3 Service层实现

到此,调用mapper接口能得到的数据结构是这样的,即

List<TreeVo>

,但childrenTreeNodes为null
在这里插入图片描述

在Service层,要做的就是把

List<TreeVo>子节

的childrenTreeNodes属性处理好,也就是把节点的子节点找到,并一个个add到进其父的

List<TreeVo> childrenTreeNodes

属性。

//Service层接口publicinterfaceTreeService{publicList<TreeVo>queryTreeNodes(String id);}

Service层实现类!!继递归查询后的又一个核心操作:

@Slf4j@ServicepublicclassTreeServiceImplimplementsTreeService{@AutowiredTreeMapper treeMapper;publicList<TreeVo>queryTreeNodes(String id){List<TreeVo> poList = treeMapper.selectTreeNodes(id);//排除根节点后,将list转map,以备后面使用Map<String,TreeVo> mapTemp = poList.stream().filter(item->!id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value,(key1, key2)-> key2));//定义最终要返回的list空集合List<TreeVo> treeVoList =newArrayList<>();//依次遍历每个元素,排除根节点
        poList.stream().filter(item->!id.equals(item.getId())).forEach(item->{//父节点是我们传入的id,即父节点是1,如1-1前端开发,那就塞进Listif(item.getParentid().equals(id)){
                treeVoList.add(item);}//找到当前节点的父节点TreeVo parentPo = mapTemp.get(item.getParentid());if(parentPo!=null){if(parentPo.getChildrenTreeNodes()==null){
                    parentPo.setChildrenTreeNodes(newArrayList<TreeVo>());}//下边开始往ChildrenTreeNodes属性的list集合中加入这个子节点
                parentPo.getChildrenTreeNodes().add(item);}});return treeVoList;}}//实现思路总之就是,如果某条数据的pid是传入的id,则放到返回的集合中//如果该数据的pid不是传入的id,那就找到其父节点,并将这个数据add到其父节点的List<TreeVo> childrenTreeNodes;

3.4、完善Controller层

@Slf4j@RestControllerpublicclassTreeInfocontroller{@AutowierdTreeService treeService;@GetMapping("/tree/info/list")publicList<TreeVo>queryTreeNodes(){return treeService.queryTreeNodes("1");}}

返回部分结果:

{
"id" : "1-2","isLeaf" : null,"isShow" : null,"label" : "移动开发","name" : "移动开发","orderby" : 2,"parentid" : "1","childrenTreeNodes" : [
               {
                  "childrenTreeNodes" : null,"id" : "1-2-1","isLeaf" : null,"isShow" : null,"label" : "微信开发","name" : "微信开发","orderby" : 1,"parentid" : "1-2"
               },
               {
                  "childrenTreeNodes" : null,"id" : "1-2-2","isLeaf" : null,"isShow" : null,"label" : "app开发","name" : "app开发","orderby" : 1,"parentid" : "1-2"
               }
               ]
 }

4、另一种思路

最后说明下,并不是一遇见树形结构就要递归,具体怎么实现,要看具体需求。比如:

需求:返回一级二级节点信息给前端,不用返回二级节点的子节点信息

在这里插入图片描述

此时其实不用自连结查询也不用递归查,直接在mapper层定义一个根据pid查询所有子类List的方法,在service层调用,并将该方法的返回值set成vo的ChildrenTreeNodes即可。具体实现:

@DatapublicclassTreeVoextendsTreePo{//此时,不考虑二级属性的子节点,//因此childrenTreeNodes类型为TreePo,不再用TreeVoList<TreePo> childrenTreeNodes;}

Mapper层代码:

//Mapper接口@Select("select * from tree where pid = #{pid}")List<TreePo> selectByPid(String pid);

Controller和Service接口定义略过了,直接写Service层的接口实现的示意代码:

//传入根节点id,如1publicList<TreeVo>queryTreeNodes(String id){List<TreeVo> voList =newArrayList<>();//一级信息,各省份数据到手List<TreePo> poList = treeMapper.selectByPid(id);for(TreePo po: poList){//由省份id查就是该省的所有市级信息List<TreePo> poTwoList = treeMapper.selectByPid(po.getId());TreeVo vo =newTreeVo();BeanUtils.copyProperties(po,vo);//二级信息set进childrenTreeNodes属性
        vo.setChildrenTreeNodes(poTwoList);
        voList.add(vo);}return voList;}
标签: java 开发语言

本文转载自: https://blog.csdn.net/llg___/article/details/130881120
版权归原作者 -代号9527 所有, 如有侵权,请联系我们删除。

“树形结构的表设计与Java接口实现”的评论:

还没有评论