巩固web阶段知识,完成后台管理系统中的课程管理模块,该模块包含了添加课程、配置课程相关信息、管理课程章节等功能。
项目地址:课程管理模块
前后端分离的核心思想就是前端HTML页面通过AJAX调用后端的API接口,并通过JSON数据进行交互。
在我们的项目中使用的是前后端分离开发方式,需要由前后端工程师共同定义接口,编写接口文档,之后大家都根据这个接口文档进行开发,到项目结束前都要一直进行接口文档的维护。
一个接口的描述至少包括下面几项:
名称: findCourseList
描述: 根据条件查询课程信息
URL: http://localhost:8080/lagou_edu_home/course/
请求方式: GET
请求参数
methodName:"findCourseList";
响应结果
{
"status": "0",
"msg": "success"
}
前端技术 | 说明 |
---|---|
Vue.js | 是一套用于构建用户界面的渐进式JavaScript框架 |
Element UI库 | element-ui 是饿了么前端出品的基于 Vue.js的后台组件库, 方便程序员进行页面快速布局和构建 |
node.js | 简单的说 Node.js 就是运行在服务端的 JavaScript 运行环境 |
axios | 对ajax的封装, 简单来说就是ajax技术实现了局部数据的刷新,axios实现了对ajax的封装 |
后端技术 | 说明 |
---|---|
Web层 | a) Servlet:前端控制器 b) Filter:过滤器 c) BeanUtils:数据封装 |
Service层 | a) 业务处理 |
dao层 | a) Mysql:数据库 b) Druid:数据库连接池 c) DBUtils: 操作数据库 |
使用Maven快速构建工程,New Module 项目名为: lagou_edu_home
创建Maven项目
见项目地址pom.xml文件
见项目地址utils文件夹、resources文件夹内容
导入表对应的实体类
Course.java
package com.lagou.pojo;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* 课程类
* */
@Data
public class Course implements Serializable {
//使用 JSONField 设置ordinal的值,来对转换成的JSON数据进行排序
//课程ID
@JSONField(ordinal = 1)
private int id;
//课程名称
@JSONField(ordinal = 2)
private String course_name;
//课程介绍
@JSONField(ordinal = 3)
private String brief;
//讲师名称
@JSONField(ordinal = 4)
private String teacher_name;
//讲师介绍
@JSONField(ordinal = 5)
private String teacher_info;
//课程原价
@JSONField(ordinal = 6)
private double price;
//原价标签
@JSONField(ordinal = 7)
private String price_tag;
//课程优惠价
@JSONField(ordinal = 8)
private double discounts;
//课程概述
@JSONField(ordinal = 9)
private String preview_first_field;
//课程概述第二个字段
@JSONField(ordinal = 10)
private String preview_second_field;
//分享图片url
@JSONField(ordinal = 11)
private String course_img_url;
//分享标题
@JSONField(ordinal = 12)
private String share_title;
//分享描述
@JSONField(ordinal = 13)
private String share_description;
//课程描述
@JSONField(ordinal = 14)
private String course_description;
//排序
@JSONField(ordinal = 15)
private int sort_num;
//课程状态,0-草稿,1-上架
@JSONField(ordinal = 16)
private int status;
//创建时间
@JSONField(ordinal = 17)
private String create_time;
//修改时间
@JSONField(ordinal = 18)
private String update_time;
//是否删除
@JSONField(ordinal = 19)
private int isDel;
@JSONField(ordinal = 20)
private String share_image_title; //分享图title
//使用JSONField(serialize = false)排除不需要转换的字段
@JSONField(serialize = false)
private int total_course_time; //课时数
@JSONField(serialize = false)
private int sales; //显示销量
@JSONField(serialize = false)
private int actual_sales; //真实销量
@JSONField(serialize = false)
private int is_new; //是否新品
@JSONField(serialize = false)
private String is_new_des; //广告语
@JSONField(serialize = false)
private int last_operator_id; //最后操作者
@JSONField(serialize = false)
private int total_duration; //总时长
@JSONField(serialize = false)
private long course_type; //课程类型
@JSONField(serialize = false)
private String last_notice_time; //最后课程最近通知时间
@JSONField(serialize = false)
private long is_gray; //是否是灰度课程
@JSONField(serialize = false)
private long grade; //级别
}
1) Lombok介绍
在项目中使用Lombok可以减少很多重复代码的书写,比如getter/setter/toString等方法的编写。
2) IDEA中安装 lombok插件
打开IDEA的Setting –> 选择Plugins选项 –> 搜索lombok –> 点击安装 –> 安装完成重启IDEA
3) 添加依赖
在项目中添加Lombok依赖jar,在pom文件中添加如下部分
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.0version>
<scope>providedscope>
dependency>
4) Lombok常用注解
@Getter/@Setter: 作用类上,生成所有成员变量的getter/setter方法
@ToString : 作用于类,覆盖默认的toString()方法 ,可以通过of属性限定显示某些字段,通过exclude属性排除某些字段
@AllArgsConstructor:生成全参构造器
@NoArgsConstructor:生成无参构造器
@Data: 该注解使用在类上,该注解会提供 getter
、setter
、equals
、hashCode
、toString
方法。
通过 @JSONField 我们可以自定义字段的名称进行输出,并控制字段的排序,还可以进行序列化标记。
Fastjson 简明教程
需求分析、数据库表分析、实体类设计、Dao接口及实现类编写、Service接口及实现类编写、Servlet编写。
其中Servlet层有技巧:BaseServlet简化Servlet操作
页面分析,需要展示哪些数据
修改CourseDao,添加 findCourseList 方法
选中要包裹代码 + Ctrl + Alt +t
接口 CourseDao
//查询课程列表信息
public List<Course> findCourseList();
实现类 CourseDaoImpl
@Override
public List<Course> findCourseList() {
try {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "SELECT id,course_name,price,sort_num,STATUS FROM course where id_del = ?";
//3.执行查询
List<Course> courseList = qr.query(sql, new BeanListHandler<Course>(Course.class), 0);
return courseList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
逻辑删除
物理删除
修改CourseService 添加 findCourseList 方法
接口 CourseService
public List<Course> findCourseList();
实现类 CourseServiceImpl
//创建 CourseDao
CourseDao courseDao = new CourseDaoImpl();
@Override
public List<Course> findCourseList() {
//调用Dao 进行查询
return courseDao.findCourseList();
}
接口开发规范
我们在做的是一个前后端分离项目、需要通过接口文档对接的项目。所以开发过程中要仔细查看前端所需的api接口和参数字段。
为了严格按照接口进行开发,提高效率,对请求及响应格式进行规范化。
开发规范 |
---|
1、get 请求时,采用key/value格式请求,Servlet中可以使用 getParameter() 获取。 |
2、post请求时有三种数据格式 第一种: Json数据,json类型的数据 Servlet中使用 fastjson进行解析 第二种: 提交form表单数据 第三种: 文件等多部件类型(multipart/form-data) |
3、响应结果统一格式为json |
为什么使用JSON?
数据格式比较简单,易于读写,JSON格式能够直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,但是完成的任务不变,且易于维护。
JSON 语法
本项目使用的是 JSON解析工具为阿里巴巴的fastjson,maven工程导入下面的依赖即可。
Fastjson 简明教程
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.1.37version>
dependency>
<dependency>
<groupId>com.colobugroupId>
<artifactId>fastjson-jaxrs-json-providerartifactId>
<version>0.3.1version>
dependency>
接口文档
前端的开发基于服务端编写的接口,如果前端人员等待服务端人员将接口开发完毕再去开发前端内容这样做效率是非常低下的,所以当接口定义完成,可以使用工具生成接口文档,前端人员查看接口文档即可进行前端开发,这样前端和服务人员并行开发,大大提高了生产效率。
接口 查询课程列表信息
- 名称: findCourseList
- 描述: 查询课程列表信息
- URL: http://localhost:8080/lagou_edu_home/course
- 请求方式: GET
- 请求参数
字段 说明 类型 是否必须 备注 methodName 要访问的功能名 String 是 该字段必须填写,用来确定要访问是
哪一个的方法
- 请求参数示例:
methodName: "findCourseList"
- 响应结果
字段 说明 类型 是否必须 备注 id 课程id int 是 course_name 课程名称 String 是 price 课程价格 double 是 课程的原价格 sort_num 课程排序 int 是 数字越大,越排在后面 status 课程状态 int 是 0-草稿,1-上架
- 响应结果示例
[{ "id": 1, "course_name": "32个Java面试必考点", "price": 8000, "sort_num": 1, "status": 1 }]
编写CourseServlet
在CourseServlet中添加 findCourseList方法
@WebServlet("/course")
public class CourseServlet extends BaseServlet {
//查询课程信息列表
public void findCourseList(HttpServletRequest request, HttpServletResponse response){
try {
//1.接收参数
//2.业务处理
CourseService cs = new CourseServiceImpl();
List<Course> courseList = cs.findCourseList();
//3.响应结果
//SimplePropertyPreFilter 指定要转换的JSON字段
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,
"id","course_name","price","sort_num","status");
String result = JSON.toJSONString(courseList,filter);
response.getWriter().print(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
SimplePropertyPreFilter filter = new SimplePropertyPreFilter(类名.class,"字段1","字段2");
JSON.toJSONString(object,filter);
(object是Java对象)http://localhost:8080/lagou_edu_home/course?methodName=findCourseList
多条件查询要注意多个参数情况下SQL的编写
where默认为1=1,这样用户即使不选择任何条件,sql查询也不会出错;如果还选择了其他的条件,就不断在where条件后追加 and语句就行了。
如果不用1=1的话,每加一个条件,都要判断前面有没有where 条件,如果没有就写where,有就写and语句,因此用1=1简化了应用程序的复杂度。
Dao层编写
实现类
/**
* 根据课程名称 课程状态 查询课程信息
* */
//根据条件查询课程信息
@Override
public List<Course> findByCourseNameAndStatus(String courseName, String status) {
try {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL 当前的查询为多条件不定项查询
//2.1 创建StringBuffer 对象,将SQL字符串 添加进缓冲区
StringBuffer sb = new StringBuffer("SELECT id,course_name,price,sort_num,STATUS FROM course WHERE 1=1 and is_del = ? ");
//2.2 创建list集合 保存参数
List<Object> list = new ArrayList<>();
list.add(0);
//2.3 判断传入的参数是否为空
if(courseName != null && courseName != ""){
sb.append(" AND course_name LIKE ?");
//like查询 需要拼接 %
courseName = "%"+courseName+"%";
//将条件放进list集合
list.add(courseName);
}
if(status != null && status != ""){
sb.append("AND STATUS = ?");
//将status 转换为 int
int i = Integer.parseInt(status);
list.add(i);
}
//执行查询
List<Course> courseList = qr.query(sb.toString(), new BeanListHandler<Course>(Course.class), list.toArray());
//返回结果
return courseList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
文字部分:
Dao层编写
接口
//保存课程营销信息
public int saveCourseSalesInfo(Course course);
实现类
//保存课程营销信息
@Override
public int saveCourseSalesInfo(Course course) {
try {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "INSERT INTO course(\n" +
"course_name,\n" +
"brief,\n" +
"teacher_name,\n" +
"teacher_info,\n" +
"preview_first_field,\n" +
"preview_second_field,\n" +
"discounts,\n" +
"price,\n" +
"price_tag,\n" +
"share_image_title,\n" +
"share_title,\n" +
"share_description,\n" +
"course_description,\n" +
"course_img_url,\n" +
"STATUS,\n" +
"create_time,\n" +
"update_time\n" +
")VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);";
//3.准备参数
Object[] param = {course.getCourse_name(),course.getBrief(),course.getTeacher_name(),course.getTeacher_info(),
course.getPreview_first_field(),course.getPreview_second_field(),course.getDiscounts(),course.getPrice(),
course.getPrice_tag(),course.getShare_image_title(),course.getShare_title(),course.getShare_description(),
course.getCourse_description(),course.getCourse_img_url(),course.getStatus(),course.getCreate_time(),course.getUpdate_time()};
//4.执行插入操作
int row = qr.update(sql, param);
return row;
} catch (SQLException e) {
e.printStackTrace();
return 0;
}
}
Service层编写
编写枚举类,设置响应状态码(放在base文件夹下)
Java 枚举(enum)
枚举类的使用方法
public enum StatusCode {
SUCCESS(0,"success"),
FAIL(1,"fail");
//定义属性
private int code;
private String message;
//定义构造
StatusCode(int code, String message) {
this.code = code;
this.message = message;
}
//get/set
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
//重写toString,将枚举对象转化为JSON
@Override
public String toString() {
JSONObject object = new JSONObject();
object.put("status",code);
object.put("msg",message);
return object.toString();
}
}
编写Service
接口
public String saveCourseSalesInfo(Course course);
实现类
@Override
public String saveCourseSalesInfo(Course course) {
//1.补全课程信息
String dateFormart = DateUtils.getDateFormart();
course.setCreate_time(dateFormart);
course.setUpdate_time(dateFormart);
course.setStatus(0);
//2.调用Dao进行插入
int i = courseDao.saveCourseSalesInfo(course);
if(i > 0){
//保存成功
String result = StatusCode.SUCCESS.toString();
return result; // {"msg":"success","status":0}
}else{
//保存失败
String result = StatusCode.FAIL.toString();
return result; // {"msg":"fail","status":1}
}
}
文件上传部分:
在添加课程营销信息的表单中,有一个图片上传项
文件上传的实质:文件的拷贝
客户端编码:
文件上传三要素:
注意: 默认情况下,表单的enctype的值是application/x-www-form-urlencoded,不能用于文件上传,只有使用了multipart/form-data,才能完整的传递文件数据
服务端编码:
导入依赖
FileUpload包可以很容易地将文件上传到你的Web应用程序
IOUtils封装了Java中io的常见操作,使用十分方便 ,需要下载 commons-io-1.4.jar 包
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>1.4version>
dependency>
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.2.1version>
dependency>
FileUpload 核心类介绍
类名 | 介绍 |
---|---|
DiskFileItemFactory | 磁盘文件项工厂, 读取文件时相关的配置,比如: 缓存的大小 , 临时目录的位置 |
ServletFileUplaod | 文件上传的一个核心类 |
FileItem | 代表每一个表单项 |
文件上传的API的详解
方法 | 说明 |
---|---|
isMultipartContent(request); | 判断是否是一个文件上传的表单 |
parseRequest(request); | 解析request获得表单项的集合 |
setHeaderEncoding(“UTF-8”); | 设置上传的文件名的编码方式 |
方法 | 说明 |
---|---|
isFormField() | 判断是否是普通表单项 |
getFieldName() | 获得表单的name属性值 |
item.getString() | 获得表单的value值 |
getName() | 获得上传文件的名称 |
getInputStream() | 获得上传文件 |
delete() | 删除临时文件 |
文件上传后台代码编写
FileUpload使用步骤:
1、创建磁盘文件项工厂
2、创建文件上传的核心类
3、解析request获得文件项集合
4、遍历文件项集合
5、判断普通表单项/文件上传项
@WebServlet("/upload")
public class FileUploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//1.创建磁盘文件项工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//2.创建文件上传核心类
ServletFileUpload upload = new ServletFileUpload(factory);
//2.1 设置上传文件名的编码
upload.setHeaderEncoding("utf-8");
//2.2 判断表单是否是文件上传表单
boolean multipartContent = upload.isMultipartContent(req);
//2.3 是文件上传表单
if(multipartContent){
//3. 解析request ,获取文件项集合
List<FileItem> list = upload.parseRequest(req);
if(list != null){
//4.遍历获取表单项
for (FileItem item : list) {
//5. 判断是不是一个普通表单项
boolean formField = item.isFormField();
if(formField){
//普通表单项, 当 enctype="multipart/form-data"时, request的getParameter()方法 无法获取参数
String fieldName = item.getFieldName();
String value = item.getString("utf-8");//设置编码
System.out.println(fieldName + "=" + value);
}else{
//文件上传项
//文件名
String fileName = item.getName();
//避免图片名重复 拼接UUID
String newFileName = UUIDUtils.getUUID()+"_"+ fileName;
//获取输入流
InputStream in = item.getInputStream();
//创建输出流 输出到H盘
FileOutputStream fos = new FileOutputStream("H:/upload/" +newFileName);
//使用工具类IOUtils,copy文件
IOUtils.copy(in,fos);
//关闭流
fos.close();
in.close();
}
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
图片若存到服务器磁盘上,不方便访问;上传到tomcat服务器则可以通过外部加载图片。
将项目部署到webapps
将部署方式改变为 war模式,把项目部署在tomcat的webapps下
在webapps中创建upload目录
upload目录专门用来保存上传过来的图片
修改代码,将图片上传到服务器
//获取上传文件的内容
InputStream in = item.getInputStream();
// 动态获取项目的运行目录
String path = this.getServletContext().getRealPath("/");
// 截取到 webapps路径
String webappsPath = path.substring(0, path.indexOf("lagou_edu_home"));
// 拼接输出路径
OutputStream out = new FileOutputStream(webappsPath+"/upload/"+newFileName);
//拷贝文件到服务器
IOUtils.copy(in,out);
out.close();
in.close();
页面加载图片
将tomcat作为图片服务器使用时,存储上传的图片后,如果想要图片可以访问,需要在idea中进行配置:
上传一张图片到服务器
在项目内部页面加载图片
http://localhost:8080/upload/abbd99891af442a8a9cb65848744452e_qiyu.jpg
BeanUtils 是 Apache commons组件的成员之一,主要用于简化JavaBean封装数据的操作。可以将一个表单提交的所有数据封装到JavaBean中。
<dependency>
<groupId>commons-beanutilsgroupId>
<artifactId>commons-beanutilsartifactId>
<version>1.8.3version>
dependency>
方法 | 描述 |
---|---|
populate(Object bean, Map properties) | 将Map数据封装到指定Javabean中, 一般用于将表单的所有数据封装到javabean |
setProperty(Object obj,String name,Object value) | 设置属性值 |
getProperty(Object obj,String name) | 获得属性值 |
Servlet编写
接口 保存&修改 课程营销信息
- 名称: courseSalesInfo
- 描述: 保存课程相关的营销信息
- URL: http://localhost:8080/lagou_edu_home/courseSalesInfo
- 请求方式: POST
- 请求参数
字段 说明 类型 是否必需 备注 id 课程id int 否 添加操作不用携带, 修改操作必须携带ID course_name 课程名称 String 是 brief 课程简介 String 是 一句话介绍课程 teacher_name 讲师名称 String 是 teacher_info 讲师介绍 String 是 preview_first_field 课程概述1 String 是 第一段描述 例如: 课程共15讲 preview_second_field 课程概述2 String 是 第二段描述 例如: 每周五更新 discounts 售卖价格 double 是 课程的售卖价格 price 商品原价 double 是 课程的原销售价 price_tag 促销文案 String 是 例如: 立即抢购 share_image_title 分享图title String 是 share_title 分享标题 String 是 share_description 分享描述 String 是 course_description 课程描述 String 是 file 文件 是
- 请求参数示例 key:value 格式
file:文件 course_name: 微服务架构 brief: 大厂架构师带你一起学 teacher_name: PDD teacher_info: 技术精湛安全驾驶30年 preview_first_field: 共5讲 preview_second_field: 每周二更新 discounts: 88.8 price: 800.0 price_tag: 先到先得 share_image_title: hello word share_title: IT修炼之路永无止境 share_description: 金牌讲师带你了解最新最牛的技术让你的实力再次进阶! course_description: 十年编程两茫茫,工期短,需求长。千行代码,Bug何处藏。纵使上线又如何,新版本,继续忙。黑白颠倒没商量,睡地铺,吃食堂。夜半梦醒,无人在身旁。最怕灯火阑珊时,手机响,心里慌.
- 响应结果
字段 说明 类型 是否必须 备注 status 表示执行成功或失败 int 是 0 表示成功, 1 表示失败 msg 响应消息 String 是
- 响应结果示例
成功 {"msg":"success","status":0} 失败 {"msg":"fail","status":1}
创建CourseSalesInfoServlet类,继承HttpServlet , 完成保存课程营销信息操作。
因为上传的信息包含文件信息,无法直接通过request直接获取参数,所以不能继承BaseServlet
小技巧:判断请求列表里是否有id,若有则为修改操作,若无则为新建操作
@WebServlet("/courseSalesInfo")
public class CourseSalesInfoServlet extends HttpServlet {
/**
* 保存课程营销信息
* 收集表单数据,封装到course对象中,将图片上传到tomcat服务器中
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//1.创建Course对象
Course course = new Course();
//2.创建Map集合,用来收集数据
Map<String,Object> map = new HashMap<>();
//3.创建磁盘工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
//4.文件上传核心对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//5.解析request对象,获取表单项集合
List<FileItem> list = fileUpload.parseRequest(req);
//6.遍历集合 判断哪些是普通的表单项,那些是文件表单项
for (FileItem item : list) {
boolean formField = item.isFormField();
if(formField){
//是普通表单项,获取表单项中的数据,保存到map
String fieldName = item.getFieldName();
String value = item.getString("UTF-8");
System.out.println(fieldName +" " + value);
//使用map收集数据
map.put(fieldName,value);
}else{
//文件上传项
//获取文件名
String fileName = item.getName();
String newFileName = UUIDUtils.getUUID()+"_"+fileName;
//获取输入流
InputStream in = item.getInputStream();
//获取webapps的目录路径
String realPath = this.getServletContext().getRealPath("/");
String wabappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));
//创建输出流
OutputStream out = new FileOutputStream(wabappsPath+"/upload/" + newFileName);
IOUtils.copy(in,out);
out.close();
in.close();
//将图片路径进行保存
map.put("course_img_url", Constants.LOCAL_URL+"/upload/" + newFileName);
}
}
//使用BeanUtils 将map中的数据封装到course对象
BeanUtils.populate(course,map);
// 业务处理
CourseService cs = new CourseServiceImpl();
String dateFormart = DateUtils.getDateFormart();
if(map.get("id") != null) {
//修改操作
//补全信息
course.setUpdate_time(dateFormart);//修改时间
String result = cs.updateCourseSalesInfo(course);
//响应结果
resp.getWriter().print(result);
}else {
//新建操作
//补全信息
course.setCreate_time(dateFormart);//创建时间
course.setUpdate_time(dateFormart);//修改时间
course.setStatus(1); //上架
String result = cs.saveCourseSalesInfo(course);
//响应结果
resp.getWriter().print(result);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
保存图片URL优化
public final class Constants {
//本地访问地址
public static final String LOCAL_URL = "http://localhost:8080";
}
//将图片路径进行保存
map.put("course_img_url", Constants.LOCAL_URL+"/upload/" + newFileName);
postman测试上传文件
Body选择form-data
key 右侧下拉选择file;value 点击Select Files选择文件,按照接口文档补全测试参数
要展示的内容是对应课程下的章节与课时信息(一门课程有多个章节,一个章节有多个课时)
Course
类与Course_Section
类是一对多关系
在 Course
类中定义一个List集合,并指定List的泛型是 Course_Section
类型,表示 一个课程中可以包含多个章节
Course类
//添加list集合 泛型是 Course_Section
List<Course_Section> sectionList = new ArrayList<>();
在 Course_Section
类中,定义一个Course
类型的属性,用来保存章节所对应的具体的课程信息
Course_Section 类
//添加一个Course类型的属性
private Course course;
Course_Section
类与 Course_Lesson
类是一对多关系
Course_Section类
//添加一个list集合 泛型是 Course_lesson
List<Course_Lesson> lessonList = new ArrayList<>();
Course_Lesson类
//添加一个Course_Section类型的属性
private Course_Section course_section;
Course_Lesson
类对象进行输出
Course_Section
类有FastJson的@JSONField注解,自动生成get/ set方法DAO层编写
我们在程序中尽量避免使用连接查询,将SQL进行拆分,每一条SQL对应一个功能
接口
//根据课程ID查询课程相关信息
public List<Course_Section> findSectionAndLessonByCourseId(int courseId);
//根据章节ID 查询章节相关的课时信息
public List<Course_Lesson> findLessonBySectionId(int sectionId);
实现类
//根据课程ID查询课程相关信息
@Override
public List<Course_Section> findSectionAndLessonByCourseId(int courseId) {
try {
//1.创建QueryRunner
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
//2.编写SQL
String sql = "SELECT \n" +
"id,\n" +
"course_id,\n" +
"section_name,\n" +
"description,\n" +
"order_num,\n" +
"STATUS\n" +
"FROM course_section WHERE course_id = ?";
//3.执行查询
List<Course_Section> sectionList = qr.query(sql, new
BeanListHandler<Course_Section>(Course_Section.class), courseId);
//4.根据章节ID查询课时信息
for (Course_Section section : sectionList) {
//调用方法 获取章节对应的课时
List<Course_Lesson> lessonList =
findLessonBySectionId(section.getId());
//将课时数据封装到 章节对象中
section.setLessonList(lessonList);
}
return sectionList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
//根据章节ID查询课时信息
@Override
public List<Course_Lesson> findLessonBySectionId(int sectionId) {
try {
QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());
String sql = "SELECT \n" +
"id,\n" +
"course_id,\n" +
"section_id,\n" +
"theme,\n" +
"duration,\n" +
"is_free,\n" +
"order_num,\n" +
"STATUS\n" +
"FROM course_lesson WHERE section_id = ?";
List<Course_Lesson> lessonList = qr.query(sql, new
BeanListHandler<Course_Lesson>(Course_Lesson.class), sectionId);
return lessonList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}