实现多级树形结构查询 比如分类(父分类、子分类)

实现多级树形结构查询 比如分类(父分类、子分类)

数据库表结构

CREATE TABLE `course_category` (
  `id` varchar(20) NOT NULL COMMENT '主键',
  `name` varchar(32) NOT NULL COMMENT '分类名称',
  `label` varchar(32) DEFAULT NULL COMMENT '分类标签默认和名称一样',
  `parentid` varchar(20) NOT NULL DEFAULT '0' COMMENT '父结点id(第一级的父节点是0,自关联字段id)',
  `is_show` tinyint DEFAULT NULL COMMENT '是否显示',
  `orderby` int DEFAULT NULL COMMENT '排序字段',
  `is_leaf` tinyint DEFAULT NULL COMMENT '是否叶子',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC COMMENT='课程分类';

仅用于分类级别固定

比如固定两级或者三级可以用 表的自连接

SELECT
	one.id one_id,
	one.label one_label,
	two.id two_id,
	two.label two_label 
FROM
	course_category one
	INNER JOIN course_category two ON one.id = two.parentid 
WHERE
	one.parentid = '1' 
	AND one.is_show = '1' 
	AND two.is_show = '1' 
ORDER BY
	one.orderby,
	two.orderby;

image-20240522162346133

递归查询方法mysql8支持(灵活)

递归并不会降低数据库的性能,但是为了避免无限递归 默认递归次数为1000次,可以通过设置cte_max_recursion_depth参数增加递归深度还可以设置max_execution_time限制执行时间,超过此时间也会终止递归操作。

根据子查到所有的父

WITH recursive t1 as(
	select * from course_category where id ='1-1-1'
	UNION all
	select course_category.* from course_category INNER JOIN t1 on t1.parentid= course_category.id
)
SELECT * from t1 order by t1.id, t1.orderby;

image-20240522162403624

根据父查到所有的子

WITH recursive t2 as(
	select * from course_category where id ='1'
	UNION all
	select course_category.* from course_category INNER JOIN t2 on t2.id= course_category.parentid
)
SELECT * from t2 order by t2.id, t2.orderby;

image-20240522162420892

java 代码实现

实体类po
package com.jhj.content.model.po;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.io.Serializable;

/**
 * <p>
 * 课程分类
 * </p>
 *
 * @author jhj
 */
@Data
@TableName("course_category")
public class CourseCategory implements Serializable {
   

    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    private String id;

    /**
     * 分类名称
     */
    private String name;

    /**
     * 分类标签默认和名称一样
     */
    private String label;

    /**
     * 父结点id(第一级的父节点是0,自关联字段id)
     */
    private String parentid;

    /**
     * 是否显示
     */
    private Integer isShow;

    /**
     * 排序字段
     */
    private Integer orderby;

    /**
     * 是否叶子
     */
    private Integer isLeaf;


}
返回信息封装
package com.jhj.content.model.dto;

import com.jhj.content.model.po.CourseCategory;
import lombok.Data;
import lombok.ToString;

import java.io.Serializable;
import java.util.List;

/**
 * @author jhj
 * @version 1.0.0
 * @ClassName CourseCategoryTreeDto.java
 * @Description 课程分类树形结构
 * @createTime 2024年05月22日 15:18:00
 */
@Data
@ToString
public class CourseCategoryTreeDto extends CourseCategory implements Serializable {
   
    List<CourseCategoryTreeDto> childrenTreeNodes;
}
mapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.jhj.content.mapper.CourseCategoryMapper">

    <!-- 通用查询映射结果 -->
    <resultMap id="BaseResultMap" type="com.jhj.content.model.po.CourseCategory">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="label" property="label" />
        <result column="parentid" property="parentid" />
        <result column="is_show" property="isShow" />
        <result column="orderby" property="orderby" />
        <result column="is_leaf" property="isLeaf" />
    </resultMap>

    <!-- 通用查询结果列 -->
    <sql id="Base_Column_List">
        id, name, label, parentid, is_show, orderby, is_leaf
    </sql>

    <select id="selectTreeNode" parameterType="string" resultType="com.jhj.content.model.dto.CourseCategoryTreeDto">
        WITH recursive t2 as(
            select * from course_category where id =#{id}
            UNION all
            select course_category.* from course_category INNER JOIN t2 on t2.id= course_category.parentid
        )
        SELECT * from t2 order by t2.id, t2.orderby;
    </select>
</mapper>
mapper
package com.jhj.content.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jhj.content.model.dto.CourseCategoryTreeDto;
import com.jhj.content.model.po.CourseCategory;

import java.util.List;

/**
 * <p>
 * 课程分类 Mapper 接口
 * </p>
 *
 * @author jhj
 */
public interface CourseCategoryMapper extends BaseMapper<CourseCategory> {
   

    //使用递归查询分类
    public List<CourseCategoryTreeDto> selectTreeNode(String id);
}
service
package com.jhj.content.service;

import com.jhj.content.model.dto.CourseCategoryTreeDto;

import java.util.List;

/**
 * @author jhj
 * @version 1.0.0
 * @ClassName CourseCategoryService.java
 * @Description 课程分类实现接口
 * @createTime 2024年05月22日 18:10:00
 */
public interface CourseCategoryService {
   
    public List<CourseCategoryTreeDto> queryTreeNodes(String id);
}
serviceImpl
package com.jhj.content.service.impl;

import com.jhj.content.mapper.CourseCategoryMapper;
import com.jhj.content.model.dto.CourseCategoryTreeDto;
import com.jhj.content.service.CourseCategoryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author jhj
 * @version 1.0.0
 * @ClassName CourseCategoryServiceImpl.java
 * @Description 课程分类实现类
 * @createTime 2024年05月22日 18:10:00
 */
@Service
@Slf4j
public class CourseCategoryServiceImpl implements CourseCategoryService {
   
    
    @Autowired
    CourseCategoryMapper courseCategoryMapper;
    
    @Override
    public List<CourseCategoryTreeDto> queryTreeNodes(String id) {
   
        List<CourseCategoryTreeDto> res=new ArrayList<>();
        //调用mapper 递归查询出分类信息
        List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNode(id);
        //找到每个节点的子节点,最终封装成List<CourseCategoryTreeDto>
        //先将list 转换为map key为节点id,value为对象 目的是方便从map 获取节点 filter(item->!id.equals(item.getId()))排除根节点
        Map<String, CourseCategoryTreeDto> map = courseCategoryTreeDtos.stream().filter(item->!id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2));
        courseCategoryTreeDtos.stream().filter(item->!id.equals(item.getId())).forEach(item->{
   
            if(item.getParentid().equals(id)){
   
                res.add(item);
            }
            //找到父节点
            CourseCategoryTreeDto courseCategoryTreeDto = map.get(item.getParentid());
            if(courseCategoryTreeDto!=null) {
   
                //如果子为空 则new一个
                if (courseCategoryTreeDto.getChildrenTreeNodes() == null) {
   
                    courseCategoryTreeDto.setChildrenTreeNodes(new ArrayList<CourseCategoryTreeDto>());
                }
                //往子里放
                courseCategoryTreeDto.getChildrenTreeNodes().add(item);
            }

        });
        //从头遍历list 一边遍历一边查找子节点放在父节点的childrenTreeNodes中

        return res;
    }
}
controller
package com.jhj.content.api;

import com.jhj.content.model.dto.CourseCategoryTreeDto;
import com.jhj.content.service.CourseCategoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @author jhj
 * @version 1.0.0
 * @ClassName CourseCategoryController.java
 * @Description 课程分类接口
 * @createTime 2024年05月13日 01:00:00
 */
@Tag(name = "课程分类管理接口",description = "课程分类管理接口")
@RestController
public class CourseCategoryController {
   
    @Autowired
    CourseCategoryService courseCategoryService;

    @Operation(summary = "课程分类树形数据")
    @GetMapping("/course-category/tree-nodes")
    public List<CourseCategoryTreeDto> queryTreeNodes(){
   

        return courseCategoryService.queryTreeNodes("1");
    }
}
接口响应结果
[
  {
   
    "id": "1-1",
    "name": "前端开发",
    "label": "前端开发",
    "parentid": "1",
    "isShow": 1,
    "orderby": 1,
    "isLeaf": 0,
    "childrenTreeNodes": [
      {
   
        "id": "1-1-1",
        "name": "HTML/CSS",
        "label": "HTML/CSS",
        "parentid": "1-1",
        "isShow": 1,
        "orderby": 1,
        "isLeaf": 1,
        "childrenTreeNodes": null
      },
      {
   
        "id": "1-1-10",
        "name": "其它",
        "label": "其它",
        "parentid": "1-1",
        "isShow": 1,
        "orderby": 10,
        "isLeaf": 1,
        "childrenTreeNodes": null
      },
      {
   
        "id": "1-1-2",
        "name": "JavaScript",
        "label": "JavaScript",
        "parentid": "1-1",
        "isShow": 1,
        "orderby": 2,
        "isLeaf": 1,
        "childrenTreeNodes": null
      },
      {
   
        "id": "1-1-3",
        "name": "jQuery",
        "label": "jQuery",
        "parentid": "1-1",
        "isShow": 1,
        "orderby": 3,
        "isLeaf": 1,
        "childrenTreeNodes": null
      },
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值