谷粒学院_day08_课程管理_添加课程之课程发布(后端开发)
终于来到添加课程的第3个步骤:课程信息的最终确认,其实这个步骤对于后端来说就是查之前第一个步骤的数据,抽象的话就是课程信息,但是说得具体点包含课程,课程简介,对应讲师,对应分类(注意,这里不查课程章节和小节),所以数据库的话我们得查4张表,查的方式有两种:单表查询和多表关联查询
》单表查询适用于需要查询的表不多的情况下,我们可以一张一张查,查完再依次设置到实体类中
》多表查询适用于需要查询的表多的情况下,因为表一多,单表查询的代码就会变多了,还要一一设置到实体类中就很麻烦,而多表关联查询可以一次性查出来我们需要的表字段,然后再设置到提前设计好的实体类就可以了。如果要说缺点就是sql语句难写,查询效率没有单表高
这次我们使用多表关联查询,首先得确认哪些表是要查的?分别是:课程表,课程简介表,讲师表,分类表(包含一级和二级分类)
表确认完之后,再考虑一个事,多表连接的话是内连接还是外连接呢?平时开发中使用最多的就是这两个了,我说下这两个的特点:
》内连接是要求全部【连接条件】必须满足,也就是全部匹配才查出来
》而外连接分为左外和右外,就不详说这两个了,只是换个方向而已。外连接是除了匹配的数据外,还保留左或右表中未匹配的数据,不过要记住一点的是,这里说未匹配的数据,只是指【连接条件】未全部满足的,而不是where和having的条件,这两者不满足的话肯定不会查出来的
在本次的课程信息查询中,我们采用左外连接,为什么呢?因为可能存在课程不存在讲师或分类,但是该课程是不是也得查出来啊;好了,就来先写sql语句先把,毕竟DAO使用mybatis,还是需要写sql语句的,如下:
SELECT ec.id,ec.title,ec.price,ec.lesson_num AS lessonNum,ec.cover, et.name AS teacherName, es1.title AS subjectLevelOne, es2.title AS subjectLevelTwo 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=#{courseId}
sql语句写完,测试也通了,剩下就是写mapper了,是不是有点激动啊?一直在使用别人提供单表查询的API,好久没写sql和mapper了吧,还是以前的老套路:写mapper接口=》mapper配置文件的编写,如下:
mapper:
public interface EduCourseMapper extends BaseMapper<EduCourse> { public CoursePublishVo getPublishCourse(String courseId); }
mapper配置文件:
<!-- 根据课程ID查询课程信息 --> <select id="getPublishCourse" parameterType="String" resultType="cn.aib.eduservice.entity.vo.CoursePublishVo"> SELECT ec.id,ec.title,ec.price,ec.lesson_num AS lessonNum,ec.cover, et.name AS teacherName, es1.title AS subjectLevelOne, es2.title AS subjectLevelTwo 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=#{courseId} </select>
然后service=》controller,接着就可以测试了;对了,由于查询出来的字段和之前表单提交上来字段不符合,就不用封装表单的实体类的,创建新的一个实体类符合给客户端,实体类我也贴一下,如下:
@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;//只用于显示 }
controller:
//根据课程ID回显最终发布的课程信息 @GetMapping("getPublishCourseInfo/{courseId}") public R updateCourseInfo(@PathVariable String courseId){ //调用service完成课程修改 //修改课程数据 CoursePublishVo publishCourse = eduCourseService.getPublishCourse(courseId); return R.ok().data("publishCourse",publishCourse); }
service:
@Override public CoursePublishVo getPublishCourse(String courseId) { //查询课程信息 CoursePublishVo publishCourse = baseMapper.getPublishCourse(courseId); return publishCourse; }
测试的话,其实还是会报错的,这个错误早在很久以前就遇见过了,异常如下:
org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): cn.aib.eduservice.mapper.EduCourseMapper.getPublishCourse
这个异常的直接原因是maven的加载机制导致的,maven项目会把src/main/java的java文件进行编译并放到类路径下,但是mapper.xml文件并不会被加载;所以改变原因就是mapper.xml没有被加载到类路径下或没有和mapper在同一文件夹下。
解决的办法有很多,有3个:
1.将maper.xml直接手动复制到类路径下(不推荐)
2.将mapper.xml放到resource下,这样的maven就能把resouce下的配置加载到类路径下,但是得保证包结构是一致的。(不推荐)
3.通过pom.xml和application.properties配置的方式完成。(推荐)
我采用第3种,配置如下:
由于考虑到其他模块也要使用到pom.xml的配置,就放到service的pom.xml下:配置的目的是让Maven能加载xml文件
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> //两个**是代表多层目录,一个*代表一级目录 </includes> <filtering>false</filtering> </resource> </resources> </build>
application.properties:
#配置mapper xml文件的路径 mybatis-plus.mapper-locations=classpath:cn/aib/eduservice/mapper/xml/*.xml //指明mapper配置文件的位置,用于读取