• 谷粒学院16万字笔记+1600张配图(十)——课程管理


    项目源码与所需资料
    链接:https://pan.baidu.com/s/1azwRyyFwXz5elhQL0BhkCA?pwd=8z59
    提取码:8z59

    demo10-课程管理

    1.课程信息确认后端

    1.1分析

    1.课程信息确认页面会显示课程名称、课程价格、课程简介、课程所属分类、课程所属讲师等等…我们要从数据表中查询这些信息,其中从edu_course表查询课程名称、课程价格;从edu_course_description表查询课程简介;从edu_subject表查询课程所属分类;从edu_teacher表查询课程所属讲师。这些数据并没有在一张数据表中,我们应该怎么解决呢?在"demo09-课程管理"的"3.1.3业务层实现类"中我们的做法是:分别查询这两张表,然后将查询到的数据封装到一个VO实体类中。此时我们也可以通过分别查询这几张数据表并封装数据来实现需求,但是在"demo09-课程管理"的"3.1.3业务层实现类"中只涉及到两张数据表,而我们此时涉及到的数据表太多了,所以不建议这样做

    2.建议通过手写sql语句来实现

    1.2多表连接查询

    1.2.1多表查询的三种方式

    在这里插入图片描述

    假如左边的数据表是课程一级分类表,右边的数据表是课程二级分类表,且右边的表的第三列存放的是课程一级分类的id

    • 内连接
      • 查询的两张表有关联的数据就叫做内连接
      • 那么查询上图中的两张表,就会查询出来四条数据:前端、后端、Java、vue
    • 左外连接
      • 查询时将查询左边表的所有数据,查询右边表时只查询和左边表有关联的数据
      • 那么查询上图中的两张表,就会查询出来五条数据:前端、后端、运维、Java、vue
    • 右外连接
      • 查询时将查询右边表的所有数据,查询左边表时只查询和右边表有关联的数据,实际上就是左外连接反过来呗
      • 那么查询上图中的两张表,就会查询出来五条数据:前端、后端、Java、vue、mysql
    1.2.2我们应该用哪种方式

    1.实际开发中我们经常用内连接和左外连接,右外连接用的不多。我们此时用内连接可以吗?当然可以,但是我们某一门课可能会没有简介,所以用内连接不太合适,所以我们这里使用左外连接查询

    2.在数据库编写如下sql命令

    SELECT ec.id,ec.title,ec.price,ec.lesson_num,
           ecd.description,
           et.name,
           es1.title AS oneSubject,
           es2.title AS twoSubject
    FROM edu_course ec LEFT OUTER JOIN edu_course_description ecd ON ec.id=ecd.id
                       LEFT OUTER JOIN edu_teacher et ON ec.teacher_id=et.id
                       LEFT OUTER JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
                       LEFT OUTER JOIN edu_subject es2 ON ec.subject_id=es2.id
    WHERE ec.id='1562267576652808193'
    

    在这里插入图片描述

    • 我们将课程一级分类和课程二级分类都存到了edu_subject这张表中,此时应该怎么做:如果一张表中有多个字段同时关联一张表,我们就需要查询多次
    • 1562267576652808193是我的edu_course数据表中的id,你们写自己数据表中的

    1.3创建vo类

    在entity–>vo包下创建vo类CoursePublishVo来封装数据

    @Data
    public class CoursePublishVo {
        private String id;
        private String title;
        private String cover;
        private Integer lessonNum;
        private String subjectLevelOne;
        private String subjectLevelTwo;
        private String teacherName;
        private String price;//只用于显示
    }
    

    在这里插入图片描述

    1.4持久层

    我们在前面的代码编写中有时只需处理控制层,有时只需处理控制层和业务层,还从来没有处理过持久层。但我们此时手写sql语句,所以需要处理持久层

    1.4.1在mapper中定义方法

    在EduCourseMapper中定义"根据课程id查询课程具体信息"的抽象方法

    public interface EduCourseMapper extends BaseMapper<EduCourse> {
        //根据课程id查询课程具体信息
        public CoursePublishVo getPublishCourseInfo(String courseId);
    }
    

    在这里插入图片描述

    1.4.2在idea中连接数据库

    1.点击"DataBase"

    在这里插入图片描述

    2.点击加号(“+”),然后点击Data Source下的MySQL

    在这里插入图片描述

    3.输入我们的mysql密码和数据库名字,然后点击"Ok"

    在这里插入图片描述

    4.填写mysql的账号密码,然后点击"Ok"

    在这里插入图片描述

    5.此时我们就可以看到数据库guli中的数据表了,说明此时连接成功

    在这里插入图片描述

    1.4.3编写映射

    在EduCourseMapper.xml中编写刚刚定义的抽象方法的映射

    <!--sql语句:根据课程id查询课程具体信息-->
    <select id="getPublishCourseInfo" resultType="com.atguigu.eduservice.entity.vo.CoursePublishVo">
        SELECT ec.id,ec.title,ec.cover,ec.lesson_num AS lessonNum,ec.price,
               es1.title AS subjectLevelOne,
               es2.title AS subjectLevelTwo,
               et.name AS teacherName
        FROM edu_course ec LEFT OUTER JOIN edu_subject es1 ON ec.subject_parent_id=es1.id
                           LEFT OUTER JOIN edu_subject es2 ON ec.subject_id=es2.id
                           LEFT OUTER JOIN edu_teacher et ON ec.teacher_id=et.id
        WHERE ec.id=#{courseId}
    </select>
    

    在这里插入图片描述

    • 为什么截图中的第6行、第7行、第8行、第9行要分别用AS lessonNumAS subjectLevelOnesubjectLevelTwoteacherName来起别名?

      • 因为我们vo类CoursePublishVo中定义的属性名是lessonNum、subjectLevelOne、subjectLevelTwo、teacher Name。所以我们需要起别名,并且起的别名一定要和CoursePublishVo中的属性名对应上
    • 我们在"1.4.1在mapper中定义方法"定义的抽象方法只有一个参数,所以截图中第13行的WHERE ec.id=#{courseId}中的courseId可以随便写(不过别写成中文啊!!!)

    • 标签中的resultType属性是方法返回值的类型,这里我们需要填写CoursePublishVo类的全路径:com.atguigu.eduservice.entity.vo,末尾再加上.CoursePublishVo

      在这里插入图片描述

    1.5控制层

    在控制器EduCourseController中定义方法

    //根据课程id查询课程具体信息
    @GetMapping("getPublishCourseInfo/{id}")
    public R getPublishCourseInfo(@PathVariable String id) {
        CoursePublishVo coursePublishVo= courseService.publishCourseInfo(id);
        return R.ok().data("publishCourse", coursePublishVo);
    }
    

    在这里插入图片描述

    1.6业务层接口

    在业务层接口EduCourseService定义抽象方法

    //根据课程id查询课程具体信息
    CoursePublishVo publishCourseInfo(String id);
    

    1.7业务层实现类

    在业务层实现类EduCourseServiceImpl中实现上一步定义的抽象方法

    //根据课程id查询课程具体信息
    @Override
    public CoursePublishVo publishCourseInfo(String id) {
        //调用mapper中的方法
        CoursePublishVo publishCourseInfo = baseMapper.getPublishCourseInfo(id);
        return publishCourseInfo;
    }
    

    在这里插入图片描述

    我们在"demo07-课程分类管理"的"7.4.2业务层实现类"的第1步说过,在业务层调用mapper中的方法我们有两种方式:使用baseMapper.xxx或使用this.xxx。但是因为我们此时是调用mapper中我们自己定义的方法,所以我们只能用BaseMapper.xxx

    1.8测试

    1.重启EduApplication服务,使用swagger进行测试

    2.在输入框输入课程id后点击"Try it out!"

    在这里插入图片描述

    3.执行了异常,说明我们接口是有问题的,解决方法在后面的"1.9加载问题"

    在这里插入图片描述

    1.9加载问题

    1.9.1分析问题

    1.接口报错如下

    org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.atguigu.eduservice.mapper.EduCourseMapper.getPublishCourseInfo
    

    在这里插入图片描述

    2.出现这种报错我们首先查看EduCourseMapper.xml的