4 课程分类查询

4 课程分类查询

4.1 需求分析

下边根据内容管理模块的业务流程,下一步要实现新增课程,在新增课程界面,有三处信息需要选择,如下图:

课程等级、课程类型来源于数据字典表,此部分的信息前端已从系统管理服务读取。

课程分类信息没有在数据字典表中存储,而是由单独一张课程分类表,存储在内容管理数据库中。

下边看下course_category课程分类表的结构

这张表是一个树型结构,通过父结点id将各元素组成一个树。

我们可以看下该表的数据,下图是一部分数据:

现在的需求是需要在内容管理服务中编写一个接口读取该课程分类表的数据,组成一个树型结构返回给前端。

课程分类的PO类如下:

如果没有此po类则需要生成的此表的po类拷贝到内容管理模块的model工程中,将mapper拷贝到内容管理模块的service工程中。

4.2 接口定义

我们可以点击新增课程,观察前端的请求记录:

http://localhost:8601/api/content/course-category/tree-nodes 该地址正是前端获取课程分类的接口地址。

通过上图界面的内容可以看出该接口的协议为:HTTP GET

请求参数为空。

通过查阅接口文档,此接口要返回全部课程分类,以树型结构返回,如下所示。

JSON
 [
         {
            "childrenTreeNodes" : [
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-1",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "HTML/CSS",
                  "name" : "HTML/CSS",
                  "orderby" : 1,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-2",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "JavaScript",
                  "name" : "JavaScript",
                  "orderby" : 2,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-3",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "jQuery",
                  "name" : "jQuery",
                  "orderby" : 3,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-4",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "ExtJS",
                  "name" : "ExtJS",
                  "orderby" : 4,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-5",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "AngularJS",
                  "name" : "AngularJS",
                  "orderby" : 5,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-6",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "ReactJS",
                  "name" : "ReactJS",
                  "orderby" : 6,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-7",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "Bootstrap",
                  "name" : "Bootstrap",
                  "orderby" : 7,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-8",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "Node.js",
                  "name" : "Node.js",
                  "orderby" : 8,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-9",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "Vue",
                  "name" : "Vue",
                  "orderby" : 9,
                  "parentid" : "1-1"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-1-10",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "其它",
                  "name" : "其它",
                  "orderby" : 10,
                  "parentid" : "1-1"
               }
            ],
            "id" : "1-1",
            "isLeaf" : null,
            "isShow" : null,
            "label" : "前端开发",
            "name" : "前端开发",
            "orderby" : 1,
            "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" : "iOS",
                  "name" : "iOS",
                  "orderby" : 2,
                  "parentid" : "1-2"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-2-3",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "手游开发",
                  "name" : "手游开发",
                  "orderby" : 3,
                  "parentid" : "1-2"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-2-4",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "Swift",
                  "name" : "Swift",
                  "orderby" : 4,
                  "parentid" : "1-2"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-2-5",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "Android",
                  "name" : "Android",
                  "orderby" : 5,
                  "parentid" : "1-2"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-2-6",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "ReactNative",
                  "name" : "ReactNative",
                  "orderby" : 6,
                  "parentid" : "1-2"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-2-7",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "Cordova",
                  "name" : "Cordova",
                  "orderby" : 7,
                  "parentid" : "1-2"
               },
               {
                  "childrenTreeNodes" : null,
                  "id" : "1-2-8",
                  "isLeaf" : null,
                  "isShow" : null,
                  "label" : "其它",
                  "name" : "其它",
                  "orderby" : 8,
                  "parentid" : "1-2"
               }
            ],
            "id" : "1-2",
            "isLeaf" : null,
            "isShow" : null,
            "label" : "移动开发",
            "name" : "移动开发",
            "orderby" : 2,
            "parentid" : "1"
         }
   ]

上边的数据格式是一个数组结构,数组的元素即为分类信息,分类信息设计两级分类,第一级的分类信息示例如下:

JSON
"id" : "1-2",
"isLeaf" : null,
"isShow" : null,
"label" : "移动开发",
"name" : "移动开发",
"orderby" : 2,
"parentid" : "1"

第二级的分类是第一级分类中childrenTreeNodes属性,它是一个数组结构:

JSON
{
"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"
               }
 }

所以,定义一个DTO类表示分类信息的模型类,如下:

Java
package com.xuecheng.content.model.dto;

import com.xuecheng.content.model.po.CourseCategory;
import lombok.Data;

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

/**
 * @description 课程分类树型结点dto
 * @author Mr.M
 * @date 2022/9/7 15:16
 * @version 1.0
 */
@Data
public class CourseCategoryTreeDto extends CourseCategory implements Serializable {

  List<CourseCategoryTreeDto> childrenTreeNodes;
}

接口定义如下:

Java
package com.xuecheng.content.api;

import com.xuecheng.content.model.dto.CourseCategoryTreeDto;
import com.xuecheng.content.service.CourseCategoryService;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * <p>
 * 数据字典 前端控制器
 * </p>
 *
 * @author itcast
 */
@Slf4j
@RestController
public class CourseCategoryController {

    
    @GetMapping("/course-category/tree-nodes")
    public List<CourseCategoryTreeDto> queryTreeNodes() {
       return null;
    }
}

4.3 接口开发

4.3.1 树型表查询

课程分类表是一个树型结构,其中parentid字段为父结点ID,它是树型结构的标志字段。

如果树的层级固定可以使用表的自链接去查询,比如:我们只查询两级课程分类,可以用下边的SQL

Java
select
       one.id            one_id,
       one.name          one_name,
       one.parentid      one_parentid,
       one.orderby       one_orderby,
       one.label         one_label,
       two.id            two_id,
       two.name          two_name,
       two.parentid      two_parentid,
       two.orderby       two_orderby,
       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

如果树的层级不确定,此时可以使用MySQL递归实现,使用with语法,如下:

Java
    WITH [RECURSIVE]
        cte_name [(col_name [, col_name] ...)] AS (subquery)
        [, cte_name [(col_name [, col_name] ...)] AS (subquery)] ...

cte_name :公共表达式的名称,可以理解为表名,用来表示as后面跟着的子查询

col_name :公共表达式包含的列名,可以写也可以不写

下边是一个递归的简单例子:

Java
with RECURSIVE t1  AS
(
  SELECT 1 as n
  UNION ALL
  SELECT n + 1 FROM t1 WHERE n < 5
)
SELECT * FROM t1;

输出:

说明:

t1 相当于一个表名

select 1 相当于这个表的初始值,这里使用UNION ALL 将初始值加入到表中。

n<5为递归执行的条件,当n>=5时结束递归调用。

下边我们使用递归实现课程分类的查询

Java
with recursive t1 as (
select * from  course_category p where  id= '1'
union all
 select t.* from course_category t inner join t1 on t1.id = t.parentid
)
select *  from t1 order by t1.id, t1.orderby

查询结果如下:

t1表中初始的数据是id等于1的记录,即根结点。

通过inner join t1 t2 on t2.id = t.parentid 找到id='1'的下级节点 。

通过这种方法就找到了id='1'的所有下级节点,下级节点包括了所有层级的节点。

上边这种方法是向下递归,即找到初始节点的所有下级节点。

如何向上递归?

下边的sql实现了向上递归:

Java
with recursive t1 as (
select * from  course_category p where  id= '1-1-1'
union all
 select t.* from course_category t inner join t1 on t1.parentid = t.id
)
select *  from t1 order by t1.id, t1.orderby

初始节点为1-1-1,通过递归找到它的父级节点,父级节点包括所有级别的节点。

以上是我们研究了树型表的查询方法,通过递归的方式查询课程分类比较灵活,因为它可以不限制层级。

mysql为了避免无限递归默认递归次数为1000,可以通过设置cte_max_recursion_depth参数增加递归深度,还可以通过max_execution_time限制执行时间,超过此时间也会终止递归操作。

mysql递归相当于在存储过程中执行若干次sql语句,java程序仅与数据库建立一次链接执行递归操作,所以只要控制好递归深度,控制好数据量性能就没有问题。

思考:如果java程序在递归操作中连接数据库去查询数据组装数据,这个性能高吗?

4.3.2 开发Mapper

下边我们可自定义mapper方法查询课程分类,最终将查询结果映射到List<CourseCategoryTreeDto>中。

生成课程分类表的mapper文件并拷贝至内容管理模块 的service工程中。

1、下边 定义一个mapper方法,并定义sql语句。

Java
public interface CourseCategoryMapper extends BaseMapper<CourseCategory> {
    public List<CourseCategoryTreeDto> selectTreeNodes(String id);
}

2、找到对应 的mapper.xml文件,编写sql语句。

Java
<select id="selectTreeNodes" resultType="com.xuecheng.content.model.dto.CourseCategoryTreeDto" parameterType="string">
    with recursive t1 as (
        select * from  course_category p where  id= #{id}
        union all
        select t.* from course_category t inner join t1 on t1.id = t.parentid
    )
    select *  from t1 order by t1.id, t1.orderby

</select>

4.3.3 开发service

定义service接口,调用mapper查询课程分类,遍历数据按照接口要求对数据进行封装

Java
public interface CourseCategoryService {
    /**
     * 课程分类树形结构查询
     *
     * @return
     */
    public List<CourseCategoryTreeDto> queryTreeNodes(String id);
}

编写service接口实现

Java
@Slf4j
@Service
public class CourseCategoryServiceImpl implements CourseCategoryService {

    @Autowired
    CourseCategoryMapper courseCategoryMapper;

    public List<CourseCategoryTreeDto> queryTreeNodes(String id) {
       List<CourseCategoryTreeDto> courseCategoryTreeDtos = courseCategoryMapper.selectTreeNodes(id);
    //将list转map,以备使用,排除根节点
    Map<String, CourseCategoryTreeDto> mapTemp = courseCategoryTreeDtos.stream().filter(item->!id.equals(item.getId())).collect(Collectors.toMap(key -> key.getId(), value -> value, (key1, key2) -> key2));
    //最终返回的list
    List<CourseCategoryTreeDto> categoryTreeDtos = new ArrayList<>();
    //依次遍历每个元素,排除根节点
    courseCategoryTreeDtos.stream().filter(item->!id.equals(item.getId())).forEach(item->{
        if(item.getParentid().equals(id)){
            categoryTreeDtos.add(item);
        }
        //找到当前节点的父节点
        CourseCategoryTreeDto courseCategoryTreeDto = mapTemp.get(item.getParentid());
        if(courseCategoryTreeDto!=null){
            if(courseCategoryTreeDto.getChildrenTreeNodes() ==null){
                courseCategoryTreeDto.setChildrenTreeNodes(new ArrayList<CourseCategoryTreeDto>());
            }
            //下边开始往ChildrenTreeNodes属性中放子节点
            courseCategoryTreeDto.getChildrenTreeNodes().add(item);
        }
    });
    return categoryTreeDtos;
    }

}

4.3.4 单元测试

定义单元测试类对service接口进行测试

Java
@SpringBootTest
class CourseCategoryServiceTests {

    @Autowired
    CourseCategoryService courseCategoryService;


    @Test
    void testqueryTreeNodes() {
        List<CourseCategoryTreeDto> categoryTreeDtos = courseCategoryService.queryTreeNodes("1");
        System.out.println(categoryTreeDtos);
    }

}

4.4 接口测试

4.4.1 接口层代码完善

完善controller方法,注入service调用业务层方法查询课程分类。

Java
package com.xuecheng.content.controller;

import com.xuecheng.content.model.dto.CourseCategoryTreeDto;
import com.xuecheng.content.model.po.Dictionary;
import com.xuecheng.content.service.CourseCategoryService;
import com.xuecheng.content.service.DictionaryService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * <p>
 * 数据字典 前端控制器
 * </p>
 *
 * @author itcast
 */
@Slf4j
@RestController
public class CourseCategoryController {

    @Autowired
    CourseCategoryService courseCategoryService;

    @GetMapping("/course-category/tree-nodes")
    public List<CourseCategoryTreeDto> queryTreeNodes() {
       return courseCategoryService.queryTreeNodes("1");
    }
}

4.4.2 测试接口

使用httpclient测试:

定义.http文件

运行测试。

完成前后端连调:

打开前端工程,进入新增课程页面。

课程分类下拉框可以正常显示

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/244917.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【jetson笔记】vscode远程调试

vscode安装插件 vscode安装远程插件Remote-SSH 安装完毕点击左侧远程资源管理器 打开SSH配置文件 添加如下内容&#xff0c;Hostname为jetson IP&#xff0c;User为登录用户名需替换为自己的 Host aliasHostName 192.168.219.57User jetson配置好点击连接&#xff0c;控制台输…

66.Spring是如何整合MyBatis将Mapper接口注册为Bean的原理?

原理 首先MyBatis的Mapper接口核心是JDK动态代理 Spring会排除接口&#xff0c;无法注册到IOC容器中 MyBatis 实现了BeanDefinitionRegistryPostProcessor 可以动态注册BeanDefinition 需要自定义扫描器&#xff08;继承Spring内部扫描器ClassPathBeanDefinitionScanner ) 重…

【计算机网络】UDP协议与TCP协议

文章目录 一、端口号1.什么是端口号2.端口号范围划分3.认识知名端口号(Well-Know Port Number)4.netstat5.pidof 二、UDP协议1.UDP协议端格式2.UDP的特点3.面向数据报4.UDP的缓冲区5.UDP使用注意事项6.基于UDP的应用层协议 三、TCP协议1.TCP协议段格式1.1理解封装解包和分用1.2…

【数学建模】插值与拟合

文章目录 插值插值方法用Python解决插值问题 拟合最小二乘拟合数据拟合的Python实现 适用情况 处理由试验、测量得到的大量数据或一些过于复杂而不便于计算的函数表达式时&#xff0c;构造一个简单函数作为要考察数据或复杂函数的近似 定义 给定一组数据&#xff0c;需要确定满…

伸向Markdown的黑手,知名博客平台曝出LFI漏洞

如果你至今依然在坚持写博客&#xff0c;在知乎或其他自媒体平台上发表文章&#xff0c;那你应该对Markdown很熟悉了。这是一种轻量级标记语言&#xff0c;借此可以用纯文本格式编写文档&#xff0c;并用简单的标记设置文档格式&#xff0c;随后即可轻松转换为具备精美排版的内…

安装宝塔面板后k8s所在节点pod无法正常工作解决方法,kubernetes k8s 与宝塔面板冲突解决方法

在实际项目过程中我们使用了k8s 在生产环境中运行管理服务。 但是对服务器的状态管理我们使用了宝塔面板进行 K8s 版本1.2.8 宝塔面板 版本 8.05 操作步骤是这样的。 1.完成1.2.8 k8s的节点安装&#xff0c;并正常运行服务。 过程略 2.安装宝塔面板 ​ yum install -y …

vue中图片不显示问题 - vue中静态资源加载

文章目录 vue中图片不显示问题静态资源URL 转换规则webpack 静态资源处理 图片不显示问题问题描述解决办法1&#xff1a;使用require引入require is not defined 解决办法2&#xff1a;使用import引入解决办法3&#xff1a;将图片放进公共文件夹static或public vue中图片不显示…

PHP中一些特征函数导致的漏洞总结

第一部分&#xff1a; 特征函数 接触到几个常用的函数&#xff1a; \\ \\\ md5 intval strpos in_array preg_match str_replacephp用这些函数实现过滤一些代码&#xff0c;漏洞可能有一些特性&#xff0c;利用这些特征代码进行对比&#xff1b;账号密码对比&#xff1b;强制检…

ChatGPT无法胜任的五种编程任务

我喜欢把ChatGPT看作是StackOverflow的智能版&#xff0c;它大有帮助&#xff0c;但短期内不会取代专业人士。作为一名前数据科学家&#xff0c;ChatGPT问世后&#xff0c;我花了大量时间来试用它。其编程能力确实给我留下了深刻的印象。它可以从零开始生成非常有用的代码&…

核桃的数量---蓝桥杯

思路&#xff1a; 题目所代表的意思就是a,b,c这三个必须是核桃数量的因子&#xff0c;即a,b,c三个的最小公倍数 #include <iostream> #include <algorithm> using namespace std; // int main() { int a,b,c;cin>>a>>b>>c;int da*b/__gcd(a,b…

大数据处理,Pandas与SQL高效读写大型数据集

大家好&#xff0c;使用Pandas和SQL高效地从数据库中读取、处理和写入大型数据集&#xff0c;以实现最佳性能和内存管理&#xff0c;这是十分重要的。 处理大型数据集往往是一项挑战&#xff0c;特别是在涉及到从数据库读取和写入数据时。将整个数据集加载到内存中的传统方法可…

antdesignvue中使用VNode写法

1、使用场景 如图&#xff1a;消息提示框中&#xff0c;将数据中的数据单独一行显示 2、代码 let errorList res.result; //后端返回的数据例&#xff1a; ["1. 数据格式不正确","2. 数据已存在"]if(errorList&&errorList.length!0){this.$notif…

k8s的图形化工具---rancher

声明式&#xff1a;yaml文件 陈述式&#xff1a;命令行 k8s的图形化工具---rancher racher是一个开源的企业级多集群的k8s关联平台。 rancher和k8s区别&#xff1a; 都是为了容器的调度和编排系统&#xff0c;但是rancher不仅能调度&#xff0c;还能管理k8s集群&#xff0…

mac电脑安卓文件传输工具:Android File Transfer直装版

Android File Transfer&#xff08;AFT&#xff09;是一款用于在Mac操作系统上与Android设备之间传输文件。它允许用户将照片、音乐、视频和其他文件从他们的Android手机或平板电脑传输到Mac电脑&#xff0c;以及将文件从Mac上传到Android设备。 下载地址&#xff1a;https://w…

Unity New Input System 及其系统结构和源码浅析【Unity学习笔记·第十二】

转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/132534422 作者&#xff1a;CSDN|Ringleader| 主要参考&#xff1a; 官方文档&#xff1a;Unity官方Input System手册与API官方测试用例&#xff1a;Unity-Technologies/InputS…

【项目日记(四)】第一层: 线程缓存的具体实现

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

CC工具箱使用指南:【Excel导出图片】

一、简介 这是一个有点娱乐向的小工具。作用就是将Excel的内容导出成一个JPG图片&#xff0c;目前只针对Excel中的第一个sheet表。 说不出来实际作用在哪里&#xff0c;不过把一个长表快速导出图片&#xff0c;有时候也挺有意思&#xff0c;有兴趣可以试试。 二、工具参数介绍…

MySQL-进阶-索引

一、索引概述 1、介绍 2、有误索引搜索效率演示 3、优缺点 二、索引结构 1、B-Tree&#xff08;多路平衡查找树&#xff09; 2、BTree 3、Hash 三、索引分类 四、索引语法 1、语法 2、案例 五、SQL性能分析 1、查看执行频次 2、慢查询日志 3、show-profile 4、explain

【目标跟踪】多相机环视跟踪

文章目录 一、前言二、流程图三、实现原理3.1、初始化3.2、输入3.3、初始航迹3.4、航迹预测3.5、航迹匹配3.6、输出结果 四、c 代码五、总结 一、前言 多相机目标跟踪主要是为了实现 360 度跟踪。单相机检测存在左右后的盲区视野。在智能驾驶领域&#xff0c;要想靠相机实现无…

新能源、新智造、新技术、新未来2024上海国际氢能产业展览会7月魔都开展!

氢能作为一种来源丰富、绿色低碳、应用广泛的二次能源&#xff0c;是实现可再生能源大规模消纳&#xff0c;电网大规模调峰和跨季节、跨地域储能的重要途径&#xff0c;对构建我国新型电力系统和实现碳达峰碳中和目标具有重要意义。 为落实国家关于发展氢能产业的决策部署&…