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

这篇具有很好参考价值的文章主要介绍了树形结构的表设计与Java接口实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

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

再比如:

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

还有:

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

2、表结构设计

这种树形结构,其 核心字段为parentId ,即父节点id。先看上面课程信息树形结构的表设计:

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

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

MySQL表结构设计如下:

字段名 数据类型 解释 是否必须
id varchar 主键,唯一标识这条数据,也是它子节点的parentId
name varchar 分类中文名称,以后要展示在前端的信息
parentId varchar 核心字段,父节点id,它爹的名字
is_show tinyint 控制这条数据是否显示,就是逻辑删除的那个味儿 根据需求分析是否加,像全国省市区,自然不用
orderby int 排序字段,同层的节点,返回时谁先谁后 看需求,对节点展示有顺序要求时用
is_leaf tinyint 是否叶子节点,1是0否 看需求,方便后端的一个字段
nodelevel int 层级标识字段 看需求,像省市区树形,这个字段可以标识下1省级2市级3区级

3、接口实现代码

3.1 模型类与接口定义

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

//伪代码
//假设树形结构表的PO类叫TreePo
@Data
public class TreeVo extends TreePo{

	List<TreeVo> childrenTreeNodes;
}
//注意这里childrenTreeNodes类型为TreeVo,而不是TreePo,因为你儿子也有自己的儿子

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

@Slf4j
@RestController
public class TreeInfocontroller{

	@GetMapping("/tree/info/list")
	public List<TreeVo> queryTreeNodes(){
		return null;
	}
}

3.2 Mapper层开发

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

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

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

# 假设表名是t_tree
# pid即核心字段parentId
SELECT
	 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
INNER JOIN t_tree two
ON one.id = two.pid
WHERE one.pid='根节点id';
# 有一条无意义数据,叫根节点,其子节点就是前端页面的第一级数据
# 有排序字段的话继续order by one.orderbyField,two.orderbyField

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

这就查出来了前两级:

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

当树形结构层级不固定,有的两级深、有的三级深,则应MySQL递归查询

WITH RECURSIVE tem_table AS(
	SELECT * FROM  t_tree one WHERE id='0'
	UNION ALL
	SELECT two.* FROM t_tree two INNER JOIN tem_table ON tem_table.id = two.pid
)

SELECT * FROM  tem_table ;
# 有排序需求时后面继续order by tem_table.id......
- tem_table是一个表名
- 使用UNION ALL 不断将每次递归得到的数据加入到表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 $$ 
drop function if exists get_child_list$$ 
create function get_child_list(in_id varchar(10)) returns varchar(1000) 
begin 
    declare ids varchar(1000) default ''; 
    declare tempids varchar(1000); 
 
    set tempids = in_id; 
    while tempids is not null do 
        set ids = CONCAT_WS(',',ids,tempids); 
        select GROUP_CONCAT(id) into tempids from t_tree where FIND_IN_SET(pid,tempids)>0;  
    end while; 
    return ids; 
end  
$$ 
delimiter ; 

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

定义mapper接口:

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


mapper.xml文件

<select id="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
树形结构的表设计与Java接口实现

在Service层,要做的就是把List<TreeVo>子节 的childrenTreeNodes属性处理好,也就是把节点的子节点找到,并一个个add到进其父的List<TreeVo> childrenTreeNodes属性。

//Service层接口
public interface TreeService {
  
    public List<TreeVo> queryTreeNodes(String id);
}


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

@Slf4j
@Service
public class TreeServiceImpl implements TreeService {

    @Autowired
    TreeMapper treeMapper;

    public List<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 = new ArrayList<>();
    	//依次遍历每个元素,排除根节点
    	poList.stream().filter(item->!id.equals(item.getId())).forEach(item->{
	    	//父节点是我们传入的id,即父节点是1,如1-1前端开发,那就塞进List
	        if(item.getParentid().equals(id)){
	            treeVoList.add(item);
	        }
	        //找到当前节点的父节点
	        TreeVo parentPo = mapTemp.get(item.getParentid());
	        if(parentPo!=null){
	            if(parentPo.getChildrenTreeNodes() ==null){
	                parentPo.setChildrenTreeNodes(new ArrayList<TreeVo>());
	            }
	            //下边开始往ChildrenTreeNodes属性的list集合中加入这个子节点
	            parentPo.getChildrenTreeNodes().add(item);
	        }
	    });
    return treeVoList;
    }

}

//实现思路总之就是,如果某条数据的pid是传入的id,则放到返回的集合中
//如果该数据的pid不是传入的id,那就找到其父节点,并将这个数据add到其父节点的List<TreeVo> childrenTreeNodes;

3.4、完善Controller层

@Slf4j
@RestController
public class TreeInfocontroller{
	
	@Autowierd
	TreeService treeService;
	
	@GetMapping("/tree/info/list")
	public List<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、另一种思路

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

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

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

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

@Data
public class TreeVo extends TreePo{

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

Mapper层代码:

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

Controller和Service接口定义略过了,直接写Service层的接口实现的示意代码:文章来源地址https://www.toymoban.com/news/detail-466093.html

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

到了这里,关于树形结构的表设计与Java接口实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java 高效构建树形结构

    构造树形结构通常使用递归遍历元素,构造元素的子集,直至子级全部构建完成,返回父级,最终完成树的构建,这种方法的时间复杂度基本上在 O ( N 2 ) O(N^2) O ( N 2 ) ,数据量比较大的时候性能大幅下降,耗时严重。通过不断实践与优化,得出一种可将构造树形结构的性能提

    2024年02月16日
    浏览(35)
  • Java 8 中的 Stream 轻松遍历树形结构

    直接上代码

    2024年02月01日
    浏览(31)
  • Java8 Stream流处理树形结构数据

    参考资料 Java8新特性-使用Stream流递归实现遍历树形结构 ID为2,6,11的Menu 是 ID为1的Menu子节点 ID为3,4,5的Menu 是 ID为2的Menu子节点 💥 注意 是下面这种写法的一种更简单的写法

    2024年02月01日
    浏览(43)
  • 【Java数据结构 -- 实现双链表的接口方法】

    双链表是一种数据结构,其中每个节点包含 一个指向前一个节点的指针和一个指向后一个节点的指针 。由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来,因此双链表可以任意且快速的插入和删除元素。 引用接口IList,在把

    2024年01月16日
    浏览(38)
  • 【Rust 基础篇】Rust 树形结构:实现与应用

    树形结构是计算机科学中一种常见的数据结构,它具有层级结构和递归特性。在 Rust 中,我们可以使用结构体和枚举等语言特性来定义树形结构,并通过引用和所有权等机制有效地管理数据。本篇博客将详细介绍 Rust 中树形结构的实现和应用,并包含代码示例和对定义的详细

    2024年02月16日
    浏览(25)
  • JAVA开发(基于Restful的微服务第三方简易接口设计)

       一、需求背景 java后端需要提供接口服务,其中接口服务分为对内网的后台管理系统的接口,对外网的用户接口和对第三方系统的接口。这里主要讲对第三方的接口。 二、接口设计 我们可以参考微信小程序的接口,一般一个系统提供给第三方系统的接口都需要接口权限认

    2024年02月09日
    浏览(41)
  • 系统架构设计专业技能 ·结构化需求分析 - 数据流图

    现在的一切都是为将来的梦想编织翅膀,让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 实体 实体可能是: (1)人物角色: 如客户、管理员、主管、经理、老师、学生 (2)组织机构 :如银行、供应商、募捐机

    2024年02月09日
    浏览(37)
  • Element-UI控件Tree实现数据树形结构

            在前端开发中,有时会遇到所有菜单数据在同一级的情况,后端未对数据进行分级处理;但前端渲染需要是树状结构的数据,如何实现数据的树状化?将数组中通过父节点的ID与子节点的parentId关联,通过递归函数来实现。         前端框架这里使用element-ui的tree控件

    2024年02月05日
    浏览(44)
  • 【前后端的那些事】开源!前后端环境搭建+树形结构表格实现

    前言 :最近写项目,发现了一些很有意思的功能,想写文章,录视频把这些内容记录下。但这些功能太零碎,如果为每个功能都单独搭建一个项目,这明显不合适。于是我想,就搭建一个项目,把那些我想将的小功能全部整合到一起。实现搭一次环境,处处使用。 本文主要

    2024年02月01日
    浏览(35)
  • 基于Java+SpringBoot+Vue+Uniapp小程序前后端分离租房管理系统设计与实现(支持支付宝支付、有需求解析文档50页、演示视频)

    博主介绍: ✌全网粉丝5W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验✌ 博主作品: 《Java项目案例》主要基于SpringBoot+MyBatis/MyBatis-

    2024年02月06日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包