拉勾教育管理系统

项目介绍与后台系统搭建

项目架构

项目介绍

​ 拉钩教育后台管理系统,是提供给拉钩教育的相关业务人员使用的一个后台管理系统,,业务人员可以在这个后台管理系统中,对课程信息、讲师信息、 学员信息等数据进行维护。

模块介绍

打开产品需求文档,查看课程管理模块中包含的内容:

1、课程信息页面展示

2、课程营销信息配置

3、配置课时( 即课程内容管理)

前后端分离开发

前后端分离架构介绍

​ 前后端分离已成为互联网项目开发的业界标准使用方式,将前端和后端的开发进行解耦。并且前后端分离会为以后的大型分布式架构、微服务架构、多端化服务(各种客户端,比如浏览器、车载终端、安卓、IOS等)打下坚实的基础。

前后端分离的核心思想就是前端HTML页面通过AJAX调用后端的API接口,并通过JSON数据进行交互。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ii6XsXAg-1625924522822)(E:\MarkDown\拉勾笔记\前后端分离架构)]

接口文档
什么是接口文档?

​ 在我们的项目中使用的是前后端分离开发方式,需要由前后端工程师共同定义接口,编写接口文档,之后大家都根据这个接口文档进行开发,到项目结束前都要一直进行接口文档的维护。

为什么要写接口文档?

​ 1、项目开发过程中前后端工程师有一个统一的文件进行沟通交流,并行开发

​ 2、项目维护中或者项目人员更迭,方便后期人员查看、维护

接口规范是什么?

一个接口的描述至少包括下面几项:

1、名称: findCourseList

2、描述: 根据条件查询课程信息

3、URL: http://localhost:8080/lagou_edu_home/course/

4、请求方式: GET

5、请求参数

methodName:"findCourseList";

6、响应结果

{"status": "0","msg": "success"
}
前后端分离架构的优势
前后端耦合的开发方式

这种方式中 Java程序员又当爹又当妈,又搞前端,又搞后端。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3Bdo7tTk-1625924522825)(E:\MarkDown\拉勾笔记\前后端耦合开发方式)]

前后端耦合的缺陷 (以JSP为例)

1、UI出好设计图之后,前端开发工程师只负责将设计图切成HTML,需要由Java开发工程师来将HTML套成JSP页面,修改问题的时候需要双方协同开发,效率低下。

2、JSP页面必须要在支持Java的WEB服务器上运行(如Tomcat、Jetty等),无法使用Nginx等(官方宣称单实例HTTP并发高达5W),性能提升不上来。

3、第一次请求JSP,必须要在WEB服务器中编译成Servlet,第一次运行会较慢。 之后的每次请求JSP都是访问Servlet再用输出流输出的HTML页面,效率没有直接使用HTML高

前后端分离的开发方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P3YwIAPp-1625924522827)(E:\MarkDown\拉勾笔记\前后端分离开发方式)]

前后端分离的优势

1、前后端分离的模式下,如果发现Bug,可以快速定位是谁的问题,不会出现互相踢皮球的现象

2、前后端分离可以减少后端服务器的并发/负载压力。除了接口以外的其他所有HTTP请求全部转移到前端Nginx上,接口的请求则转发调用Tomcat.

3、前后端分离的模式下,即使后端服务器暂时超时或宕机了,前端页面也会正常访问,只不过数据刷不出来而已。

4、前后端分离会更加合理的分配团队的工作量,减轻后端团队的工作量,提高了性能和可扩展性。

技术选型

前端技术选型
前端技术 说明
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: 操作数据库

项目开发环境

开发工具
  • 后端: IDEA 2019
  • 前端: VS code
  • 数据库: SQLYog
开发环境
  • JDK 11
  • Maven 3.6.3
  • MySQL 5.7

Maven 项目管理工具

Maven介绍

什么是Maven

​ Maven是一个跨平台的项目管理工具。作为Apache组织的一个颇为成功的开源项目,其主要服务于基于Java平台的项目创建,依赖管理和项目信息管理。maven是Apache的顶级项目,解释为“专家,内行”,它是一个项目管理的工具,maven自身是纯java开发的,可以使用maven对java项目进行构建、依赖管理。

Maven的作用

1、依赖管理

​ 依赖指的就是是 我们项目中需要使用的第三方Jar包,一个大一点的工程往往需要几十上百个Jar包,按照我们之前的方式,每使用一种Jar,就需要导入到工程中,还要解决各种Jar冲突的问题。

​ Maven可以对Jar包进行统一的管理,包括快速引入Jar包,以及对使用的 Jar包进行统一的版本控制

2、一键构建项目

​ 之前我们创建项目,需要确定项目的目录结构,比如src 存放Java源码,resources存放配置文件,还要配置环境比如JDK的版本等等,如果有多个项目 那么就需要每次自己搞一套配置,十分麻烦

​ Maven为我们提供了一个标准化的Java项目结构,我们可以通过Maven快速创建一个标准的Java项目

Maven 的使用

Maven软件的下载

下载地址:http://maven.apache.org/download.cgi

目前最新版是 apache-maven-3.6.3 版本

Maven软件的安装

Maven 下载后,将 Maven 解压到一个没有中文没有空格的路径下。

解压后目录结构如下:

1、bin:存放了 maven 的命令

2、boot:存放了一些 maven 本身的引导程序,如类加载器等

3、conf:存放了 maven 的一些配置文件,如 setting.xml 文件

4、lib:存放了 maven 本身运行所需的一些 jar 包

Maven环境变量配置
  1. 配置 MAVEN_HOME ,变量值就是你的 maven 安装的路径(bin 目录之前一级目录)

  2. 将MAVEN_HOME 添加到Path系统变量

Maven 软件版本测试

通过 mvn -v命令检查 maven 是否安装成功,看到 maven 的版本为 3.6.3 及 java 版本为 jdk-11 即为安装 成功。 打开命令行,输入 mvn –v命令

Maven 仓库

Maven中的仓库是用来存放maven构建的项目和各种依赖的(Jar包)

Maven的仓库分类

1、本地仓库: 位于自己计算机中的仓库, 用来存储从远程仓库或中央仓库下载的插件和 jar 包

2、远程仓库: 需要联网才可以使用的仓库,阿里提供了一个免费的maven远程仓库

3、中央仓库: 在 maven 软件中内置一个远程仓库地址 http://repo1.maven.org/maven2 ,它是中央仓库,服务于整个互联网,它是由 Maven 团队自己维护,里面存储了非常全的 jar 包,它包含了世界上大部分流行的开源项目构件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GBt5km8h-1625924522830)(E:\MarkDown\拉勾笔记\Maven的仓库分类)]

Maven 本地仓库的配置
  1. maven仓库默认是在 C盘 .m2 目录下,我们不要将仓库放在C盘,所以这里同学们要重新配置一下

  2. 将“repository.rar”解压至自己的电脑上(注意最好放在没有中文及空格的目录下)

  3. 在maven安装目录中,进入 conf文件夹, 可以看到一个 settings.xml 文件中, 我们在这个文件中,进行本地仓库的配置

4)打开 settings.xml文件,进行如下配置如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TITS9EXl-1625924522832)(E:\MarkDown\拉勾笔记\Maven 本地仓库的配置)]

配置阿里云远程仓库
  1. 打开 settings.xml,找到 标签 , 下面的内容复制到 中 即可
<mirror><id>alimaven</id><name>aliyun maven</name><url>http://maven.aliyun.com/nexus/content/groups/public/</url><mirrorOf>central</mirrorOf>
</mirror>

创建Maven项目

IDEA中配置Maven
  1. 打开IDEA 创建一个新的project

  2. 起名为web_work

  3. 首先打开IDEA 选择File --> Settings --> 搜素maven

  4. 修改默认配置配置

创建Maven工程
  1. project创建好以后, 选择创建module

  2. 选中创建一个 maven 工程

  3. 点击 Next填写项目信息

Maven目录说明:

src/main/java       —— 存放项目的.java 文件
src/main/resources —— 存放项目资源文件,如数据库的配置文件
src/test/java      —— 存放所有单元测试.java 文件,如 JUnit 测试类
target             —— 项目输出位置,编译后的class文件会输出到此目录
pom.xml            —— maven 项目核心配置文件
Maven工程改造

当前创建的maven项目是一个 普通的Java项目,不是web项目,我们要进行一下改造

  1. 在main目录下创建一个webapp文件夹

  2. 选择 project Structure —> facets—> 点击+号 添加web —> 选择当前工程hello_maven

  3. 修改路径信息

4)修改为 我们的 webapp目录

  1. 点击ok 后,项目就变为了web项目,在webapp目录下再创建一个 index.jsp,就OK了
pom核心配置文件

一个 maven 工程都有一个 pom.xml 文件,通过 pom.xml 文件定义项目的信息、项目依赖、引入插件等等。

  1. 创建一个Servlet,缺少jar包报错,要解决问题,就是要将 servlet-api-xxx.jar 包放进来,作为 maven 工程应当添加 servlet的坐标,从而导入它的 jar

  2. pom.xml 文件中引入依赖包的坐标

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.lagou</groupId><artifactId>hello_maven</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>3.1.0</version></dependency></dependencies></project>
  1. 一个Maven工程就是由groupIdartifactIdversion 作为唯一标识,我们在引用其他第三方库的时候,也是通过这3个变量确定。

3.1)坐标的概念

​ 在maven中坐标就是为了定位一个唯一确定的jar包。

​ maven世界拥有大量构建,我们需要找一个用来唯一标识一个构建的统一规范,拥有了统一规范,就可以把查找工作交给机器

3.2)Maven坐标主要组成(GAV) - 确定一个jar在互联网位置

标签 含义
groupId 定义当前Maven组织名称,通常是公司名
artifactId 定义实际项目名称
version 定义当前项目的当前版本
packaging 打包类型
jar:执行 package 会打成 jar 包 war:执行 package 会打成 war 包
dependency 使用 <dependency>声明一个依赖后,Maven就会自动下载这个依赖包
  1. maven 的依赖管理,是对项目所依赖的 jar 包进行统一管理
标签 含义
dependencies 表示依赖关系
dependency 使用 <dependency>声明一个依赖后,Maven就会自动下载这个依赖包
<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>3.1.0</version></dependency>
</dependencies>
  1. 坐标的来源方式

添加依赖需要指定依赖 jar 包的坐标,但是很多情况我们是不知道 jar 包的的坐标,可以通过如下方 式查询:
从网站中搜索即可

5.1) 输入网址,进入网址 , 进行查询

https://mvnrepository.com/

5.2) 点击进入后,可以看到各个版本的信息,选择3.1.0

添加插件
  1. 添加编译插件, 设置 jdk 编译版本

本教程使用 jdk11,需要设置编译版本为 11,这里需要使用 maven 的插件来设置

在pom中加入如下配置:

 <!-- properties 是全局设置,可以设置整个maven项目的编译器 JDK版本 --><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><!-- 重点  --><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><!-- 在build中 我们需要指定一下项目的JDK编译版本,maven默认使用1.5版本进行编译注意 build 与 dependencies是平级关系,标签不要写错位置  --><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><release>11</release></configuration></plugin></plugins></build>
运行Maven项目
  1. 完善项目代码

  2. 配置tomcat,部署项目

  3. 运行项目,默认访问 index.jsp

  4. 访问Servlet

Maven的常用命令
  1. 一个maven项目生命周期

​ 使用 maven 完成项目的构建,项目构建包括:清理、编译、测试、部署等过程,maven 将这些 过程规范为一个生命周期,如下所示是生命周期的各各阶段:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ikaQKZms-1625924522834)(E:\MarkDown\拉勾笔记\maven项目生命周期)]

maven 通过执行一些简单命令即可实现上边生命周期的各个过程

命令 说明
mvn compile 完成编译操作 , 执行完毕后,会生成target目录,该目录中存放了编译后的字节码文件
mvn clean 执行完毕后,会将target目录删除
mvn test 执行完毕后,会在target目录中生成三个文件夹:
surefire、surefire-reports(测试报告)、test-classes(测试的字节码文件)
mvn package 完成打包操作, 执行完毕后,会在target目录中生成一个文件,该文件可能是 jar、war
mvn install 执行 mvn install命令,完成将打好的jar包安装到本地仓库的操作,执行完毕后,会在本地仓库中出现安装后的jar包,方便其他工程引用
  1. idea中安装好maven后,在界面左侧有一个maven视图,里面有对应的命令插件,可以执行上面表格中的命令

  2. 工具栏介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wPsY4EEQ-1625924522836)(E:\MarkDown\拉勾笔记\maven工具栏介绍)]

1.根据pom.xml文件重新导入所有Maven项目和依赖,刷新
2.创建源码(重新编译)并更新目录
3.下载源码或文档
4.添加Maven项目
5.执行生命周期中的阶段,选中lifecycle选项中生命周期中的一个阶段(phase),才能点击执行。
6.运行Maven生命周期或插件
7.切换离线模式,就是关闭和远程仓库的链接,从本地仓库中获取,也不能将jar包提交到远程仓库
8.是否跳过测试,点击选中就可以跳过测试,在点击选择取消跳过测试
9.展示当前选中的maven项目jar包的依赖,并且可以直接在图形化依赖图上进行排除依赖操作
10.收起下面展开的视图
11.跳转到maven的Setting页面
依赖范围介绍
  1. A 依赖 B,需要在 A 的 pom.xml 文件中添加 B 的坐标,添加坐标时需要指定依赖范围,依赖范围包括:
依赖范围 说明
compile 编译范围,指 A在编译时依赖 B,此范围为默认依赖范围。编译范围的依赖会用在 编译、测试、运行,由于运行时需要所以编译范围的依赖会被打包。
provided provided 依赖只有在当 JDK 或者一个容器已提供该依赖之后才使用, provided 依赖在编译和测试时需要,在运行时不需要,比如:servlet api 被 tomcat 容器提供。
runtime runtime 依赖在运行和测试系统的时候需要,但在编译的时候不需要。比如:jdbc 的驱动包。由于运行时需要所以 runtime 范围的依赖会被打包。
test test 范围依赖 在编译和运行时都不需要,它们只有在测试编译和测试运行阶段可用, 比如:junit。由于运行时不需要所以test范围依赖不会被打包。
system system 范围依赖与 provided 类似,但是你必须显式的提供一个对于本地系统中 JAR 文件的路径,需要指定 systemPath 磁盘路径,system依赖不推荐使用。
  1. 项目中添加的坐标 ,并指定依赖范围
 <dependencies><dependency><!-- 项目名称 --><groupId>javax.servlet</groupId><!-- 模块名称 --><artifactId>servlet-api</artifactId><!-- 版本信息 --><version>3.1.0</version><!-- 依赖范围, 指定依赖范围是编译与测试时有效,运行时无效,运行时使用tomcat中的依赖,避免冲突 --><scope>provided</scope></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.1.2</version><scope>provided</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><!-- 在测试时有效 --><scope>test</scope></dependency></dependencies>

后台系统搭建

课程管理模块功能分析

课程管理

实现以下功能:

​ 1、展示课程列表

​ 2、根据课程名和状态进行查询

​ 3、新建课程

​ 4、课程上架与下架

营销信息

营销信息,其实就是设置课程的详细信息

​ 1、回显课程信息

​ 2、修改课程信息,包含了图片上传

配置课时

配置课时指的是对课程下所属的章节与课时进行配置(一个课程对应多个章节,一个章节有多个课时)

​ 1、以树形结构的下拉框形式,展示课程对应的章节与课时信息

​ 2、添加章节功能

​ 3、修改章节功能

​ 4、修改章节状态功能

课程管理模块表设计

创建数据库及表

使用SQLYog 执行SQL脚本,导入数据库及表

表关系介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cHvTdDVf-1625924522838)(E:\MarkDown\拉勾笔记\拉勾教育管理系统数据库表关系)]

环境搭建

创建项目

使用Maven快速构建工程,项目名为: lagou_edu_home

  1. 选择maven,直接next

  2. 填写项目相关信息,创建maven项目

  3. 当前maven项目还不是 一个web项目,进行改造

项目目录

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s6JQdfnN-1625924522839)(E:\MarkDown\拉勾笔记\拉勾教育管理系统项目目录结构)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rptiyMj0-1625924522841)(F:\拉勾\拉勾教育管理系统\任务1\02_图片\69.jpg)]

导入pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.lagou</groupId><artifactId>lagou_edu_home</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><!-- properties 是全局设置,可以设置整个maven项目的编译器 JDK版本 --><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><!-- Servlet --><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.2</version><scope>provided</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.13</version><scope>test</scope></dependency><!--    Beanutils    --><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.8.3</version></dependency><!--   DBUtils    --><dependency><groupId>commons-dbutils</groupId><artifactId>commons-dbutils</artifactId><version>1.6</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.1.1</version></dependency><!-- 数据库相关 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.37</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.0.9</version></dependency><!--fastjson工具包 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.3</version></dependency><dependency><groupId>com.colobu</groupId><artifactId>fastjson-jaxrs-json-provider</artifactId><version>0.3.1</version></dependency><!--  文件上传 --><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>1.4</version></dependency><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.2.1</version></dependency><!--   Lombok  --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.0</version><scope>provided</scope></dependency></dependencies><build><plugins><!--  maven编译插件 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.8.1</version><configuration><release>11</release></configuration></plugin></plugins></build></project>
导入工具类及配置文件

​ 导入连接池工具类以及数据库配置文件

导入实体类

1) Lombok介绍

在项目中使用Lombok可以减少很多重复代码的书写。比如说getter/setter/toString等方法的编写。

2) IDEA中安装 lombok插件

打开IDEA的Setting –> 选择Plugins选项 –> 搜索lombok –> 点击安装 –> 安装完成重启IDEA

3) 添加依赖

在项目中添加Lombok依赖jar,在pom文件中添加如下部分

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.0</version><scope>provided</scope>
</dependency>

4) Lombok常用注解

  • @Getter/@Setter: 作用类上,生成所有成员变量的getter/setter方法
  • @ToString : 作用于类,覆盖默认的toString()方法 ,可以通过of属性限定显示某些字段,通过exclude属性排除某些字段
  • @AllArgsConstructor:生成全参构造器
  • @NoArgsConstructor:生成无参构造器
  • @Data: 该注解使用在上,该注解会提供 gettersetterequalshashCodetoString 方法。

5) 导入表对应的实体类

通用Servlet

需求分析

课程模块下有两个子模块:

1、营销信息

2、配置课时(课程内容管理)

每个模块下都有很多的功能,比如课程模块 的 新建课程,上架课程,下架课程,根据课程名查询等等功能,每一个功能都是一个Servlet.

问题: 一个功能就是一个Servlet,那么一个项目下有海量的Servlet,这种方式好吗 ?

​ Servlet太多了,不好管理,而且Servlet越多服务器运行就越慢,资源消耗就越多

Servlet对应模块

我们使用一个Servlet对应一个模块的方式进行开发

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><%--  一个模块对应一个Servlet  --%><a href="${pageContext.request.contextPath}/test?methodName=addCourse">新建课程</a><a href="${pageContext.request.contextPath}/test?methodName=findByName">根据课程名查询</a><a href="${pageContext.request.contextPath}/test?methodName=findByStatus">根据状态查询</a></body>
</html>

TestServlet

/*** 当前Servlet 对应的是课程管理模块*/
@WebServlet("/test")
public class TestServlet extends HttpServlet {/***  doGet方法作为一个调度器 根据请求功能的不同,调用对应的方法*      规定必须传递一个参数*          methodName=功能名* @param req* @param resp* @throws ServletException* @throws IOException*/@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1.获取参数 要访问的方法String methodName = req.getParameter("methodName");// 2.判断 执行对应的方法if ("addCourse".equals(methodName)){addCourse(req, resp);} else if ("findByName".equals(methodName)){findByName(req,resp);} else if ("findByStatus".equals(methodName)){findByStatus(req, resp);} else {System.out.println("请求的功能不存在!");}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}// 添加课程功能public void addCourse(HttpServletRequest req, HttpServletResponse resp){System.out.println("添加课程!");}// 根据课程名查询课程public void findByName(HttpServletRequest req, HttpServletResponse resp){System.out.println("根据课程名进行查询!");}// 根据课程状态查询课程public void findByStatus(HttpServletRequest req, HttpServletResponse resp){System.out.println("根据课程状态查询!");}
}
提高代码的可维护行

我们可以使用反射去对代码进行优化,提升代码的可维护性/可扩展性

反射的知识回顾:

​ 第一步:先获取请求携带的方法参数值

​ 第二步:获取指定类的字节码对象

​ 第三步:根据请求携带的方法参数值,再通过字节码对象获取指定的方法

​ 第四步:最后执行指定的方法

/*** 当前Servlet 对应的是课程管理模块*/
@WebServlet("/test")
public class TestServlet extends HttpServlet {/***  doGet方法作为一个调度器 根据请求功能的不同,调用对应的方法*      规定必须传递一个参数*          methodName=功能名* @param req* @param resp* @throws ServletException* @throws IOException*/@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1.获取参数 要访问的方法String methodName = req.getParameter("methodName");// 2.判断 执行对应的方法if (methodName != null){// 通过反射优化代码 提升代码的可维护性try {// 1.获取字节码文件对象  this = TestServletClass c = this.getClass();// 2.根据传入的方法名,获取对应的方法对象Method method = c.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);// 3.调用method对象的 invoke方法,执行对应的功能method.invoke(this,req,resp);} catch (Exception e) {e.printStackTrace();System.out.println("请求的功能不存在!");}}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}// 添加课程功能public void addCourse(HttpServletRequest req, HttpServletResponse resp){System.out.println("添加课程!");}// 根据课程名查询课程public void findByName(HttpServletRequest req, HttpServletResponse resp){System.out.println("根据课程名进行查询!");}// 根据课程状态查询课程public void findByStatus(HttpServletRequest req, HttpServletResponse resp){System.out.println("根据课程状态查询!");}
}
抽取通用的BaseServlet

当前代码依然存在问题:

​ 每个Servlet都需要写一份相同的反射代码

解决方案:

​ 将反射相关的代码抽取到一个类中 BaseServlet, 让BaseServlet去继承HTTPServlet

BaseServlet

public class BaseServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {//1.获取参数//获取要调用的方法名String methodName = req.getParameter("methodName");//2.业务处理if(methodName != null){//通过反射优化代码,提升代码的可维护性//1.获取字节码对象  this = TestServlet对象Class c = this.getClass();//2.根据传入的方法名, 获取对应方法对象,执行方法即可Method method = c.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);//3.调用Method对象的 invoke()方法,执行对应的功能method.invoke(this,req,resp);}} catch (Exception e) {System.out.println("请求的功能不存在! !");e.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req,resp);}
}

修改 TestServlet,继承 BaseServlet

@WebServlet("/test")
public class TestServlet extends BaseServlet {/***  在模块对应的Servlet中只保留 业务相关代码*  当有请求访问到 TestServlet时, 发现没有doGet和doPost方法,就回去父类中找,从而执行BaseServlet中的*  doGet方法* */public void addCourse(HttpServletRequest req, HttpServletResponse resp){System.out.println("新建课程");}public void findByStatus(HttpServletRequest req, HttpServletResponse resp){System.out.println("根据状态查询");}public void findByName(HttpServletRequest req, HttpServletResponse resp){System.out.println("根据课程名称查询");}
}

JSON

JSON简述

JSON(JavaScript Object Notation) JavaScript对象表示法(JSON源于JS)

JSON的特点:

  • JSON 是一种轻量级的数据交换格式。
  • JSON采用完全独立于语言的文本格式,就是说不同的编程语言JSON数据是一致的。
  • JSON易于人阅读和编写,同时也易于机器解析和生成(一般用于提升网络传输速率)。

XML与JSON的区别

  • XML : 可扩展标记语言,是一种用于标记电子文件使其具有结构性的标记语言。

  • JSON: (JavaScript Object Notation,JS 对象简谱) 是一种轻量级的数据交换格式。

  • 相同点:

    • 它们都可以作为一种数据交换格式。
  • 二者区别:

    • XML是重量级的,JSON是轻量级的,XML在传输过程中比较占带宽,JSON占带宽少,易于压缩。

    • XML和json都用在项目交互下,XML多用于做配置文件,JSON用于数据交互

    • JSON独立于编程语言存在,任何编程语言都可以去解析json

JSON语法格式

我们先来看一下JSON数据:

{"id": 110,"name": "李会长","age": 24
}

语法注意:

  1. 外面由{}括起来
  2. 数据以"键:值"对的形式出现(其中键多以字符串形式出现,值可取字符串,数值,甚至其他json对象)
  3. 每两个"键:值"对以逗号分隔(最后一个"键:值"对省略逗号
  4. 参数值如果是string类型,就必须加引号,如果是数字类型,引号可加可不加

遵守上面4点,便可以形成一个json对象。

代码示例:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>Title</title><script type="text/javascript" src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script><script>// 自定义JSON数据格式(Java中的对象)var person = {"name":"tom","sex":"女","age":12};console.log(person);// 数组格式var persons = {"person":[{"name":"tom","sex":"女","age":12},{"name":"jack","sex":"男","age":22}]};console.log(persons);// 集合var list = [{"name":"老五","sex":"女","age":12},{"name":"会长","sex":"男","age":12}];console.log(list);</script>
</head>
<body></body>
</html>

JSON数据的转换

​ 目前,前后端的ajax通讯几乎用的都是json格式的了,所以在开发的过程中,我们经常会涉及到JSON数据的转换

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nsz0AFnc-1625924522843)(E:\MarkDown\拉勾笔记\JSON数据格式的转换)]

FastJson介绍
  • Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。
  • FastJson特点如下:
    • 能够支持将java bean序列化成JSON字符串,也能够将JSON字符串反序列化成Java bean。
    • 顾名思义,FastJson操作JSON的速度是非常快的。
    • 无其他包的依赖,使用比较方便。
FastJson的使用

在Maven项目中使用FastJson库,需要提前在Maven的配置文件中添加此FastJson包的依赖

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.3</version>
</dependency><dependency><groupId>com.colobu</groupId><artifactId>fastjson-jaxrs-json-provider</artifactId><version>0.3.1</version>
</dependency>
将 Java 对象转换为 JSON 格式
  1. 定义一个名为 Person 的 JavaBean类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {private String username;private int age;private String birthday;
}
  1. 可以使用 JSON.toJSONString() 将 Java 对象转换换为 JSON 对象:
public class TestFastjson {// Java对象转换为JSON@Testpublic void javaBeanToJSON(){// 1.创建Perosn对象Person p = new Person("小斌",25, DateUtils.getDateFormart());// 2.使用JSON对象 将Perosn对象转换为 JSON数据String jsonString = JSON.toJSONString(p);System.out.println(jsonString); // {"age":25,"birthday":"2021-07-06 16:49:26","username":"小斌"}}// Java 中的集合转JSON@Testpublic void ListToJSON(){// 1.创建Perosn对象Person p1 = new Person("小斌",25, DateUtils.getDateFormart());Person p2 = new Person("小斌",25, DateUtils.getDateFormart());Person p3 = new Person("小斌",25, DateUtils.getDateFormart());// 2.创建ArrayList集合List<Person> list = new ArrayList<>();// 3.将对象封装到集合Collections.addAll(list,p1,p2,p3);// 4.使用JSON对象的 toJSONString()方法String jsonString = JSON.toJSONString(list);// [{"age":25,"birthday":"2021-07-06 16:54:02","username":"小斌"},{"age":25,"birthday":"2021-07-06 16:54:03","username":"小斌"},{"age":25,"birthday":"2021-07-06 16:54:03","username":"小斌"}]System.out.println(jsonString);}
}
  1. Fastjson中的 @JSONField 注解
  • 通过 @JSONField 我们可以自定义字段的名称进行输出,并控制字段的排序,还可以进行序列化标记。

    • 指定name属性, 字段的名称
    • 使用 ordinal属性, 指定字段的顺序
    • 使用 serialize属性, 指定字段不序列化
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {// 可以通过 name 去指定输出的名称// 可以使用 ordinal属性,指定字段输出的顺序// 使用serialize属性,指定字段是否序列化@JSONField(name="USERNAME",ordinal = 1)private String username;@JSONField(name="AGE",ordinal = 2)private int age;@JSONField(serialize = false)private String birthday;}
JSON 字符串转换为 Java 对象
  • JSON.parseObject()

    • 可以使用 JSON.parseObject() 将 JSON 字符串转换为 Java 对象。
    • 注意反序列化时为对象时,必须要有默认无参的构造函数,否则会报异常
  • JSON.parseArray()
    • 可以使用 JSON.parseArray() 将 JSON 字符串转换为 集合对象。
// JSON转Java对象@Testpublic void JSONToJava(){String json = "{\"age\":25,\"birthday\":\"2021-07-06 16:49:26\",\"username\":\"小斌\"}";// 使用JSON对象的 parseObject()Person person = JSON.parseObject(json, Person.class);System.out.println(person);}// JSON转List集合@Testpublic void JSONToList(){String json = "[{\"age\":25,\"birthday\":\"2021-07-06 16:54:02\",\"username\":\"小斌\"},{\"age\":25,\"birthday\":\"2021-07-06 16:54:03\",\"username\":\"小斌\"},{\"age\":25,\"birthday\":\"2021-07-06 16:54:03\",\"username\":\"小斌\"}]";// 使用parseArray() 方法,将JSON转换为集合List<Person> list = JSON.parseArray(json, Person.class);System.out.println(list);}

小结

1、项目架构:

项目介绍:拉勾教育后台管理系统 ----> 课程管理模块

​ 课程管理模块的功能:课程管理、营销信息、配置课时

前后端分离开发:优势:1)责任清晰;2)减少后端服务器压力;3)合理分配

接口文档:前后端工程师共同定义接口文档,按照接口文档要求进行开发

​ 项目技术选型

2、MAVEN 项目管理工具:

​ 如何安装配置maven

maven的基本使用:1)maven仓库的配置;2)使用maven构建项目;3)依赖的配置

​ maven的常用命令:1)clean; 2)compile; 3)install; 4)package

3、后台系统搭建

​ 课程管理模块需求分析

​ 课程模块表设计

​ 环境搭建

通用Servlet

4、JSON

​ 轻量级数据交换格式

​ 项目中后台响应的数据格式是json格式

​ Fastjson 的使用

课程管理模块开发

开发流程

需求分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0uk1Cmzs-1625924522844)(E:\MarkDown\拉勾笔记\课程管理模块开发需求分析)]

数据库表分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ETMdB7vQ-1625924522846)(E:\MarkDown\拉勾笔记\课程管理模块开发数据库表分析)]

实体类设计

根据数据库中的Course表,对应创建 Course.java

  1. 使用 @JSONField(ordinal = int类型的值) , 指定排序的值,生成JSON时会按照指定顺序进行排序

  2. 使用 @JSONField(serialize = false) ,排除不需要转换的字段,另外fastjson还会自动排除为空的字段

/*** 课程类* */
@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; //级别}

Dao接口及实现类编写

/*** 课程模块 DAO层接口* */
public interface CourseDao {}
/*** 课程模块 DAO层实现类* */
public class CourseDaoImpl implements CourseDao {}

Service接口及实现类编写

/***  课程模块 Service层 接口* */
public interface CourseService {}
/*** 课程模块Service层 实现类* */
public class CourseServiceImpl implements CourseService {}

CourseServlet编写

CourseServlet 要继承通用的BaseServlet

@WebServlet(name="courseServlet",value="/course")
public class CourseServlet extends BaseServlet {}

功能一:查询课程列表信息

需求分析

页面分析,需要展示哪些数据

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wI22KYXA-1625924522847)(E:\MarkDown\拉勾笔记\查询课程列表信息需求分析)]

编写代码

Dao层编写
  1. 修改CourseDao,添加 findCourseList 方法
/*** 课程模块 DAO层接口*/
public interface CourseDao {// 查询课程列表信息public List<Course> findCourseList();}/*** 课程模块的 DAO层的实现类*/
public class CourseDaoImpl implements CourseDao {@Overridepublic List<Course> findCourseList() {try {// 1.创建QueryRunnerQueryRunner qr = new QueryRunner(DruidUtils.getDataSource());// 2.编写SQL  判断是否删除 取出is_del = 0 的数据,未删除的数据String sql = "SELECT \n" +"\tid,\n" +"\tcourse_name,\n" +"\tprice,\n" +"\tsort_num,\n" +"\tSTATUS\n" +"FROM course WHERE is_del = ?;";// 3.执行查询List<Course> courseList = qr.query(sql, new BeanListHandler<Course>(Course.class), 0);return courseList;} catch (SQLException e) {e.printStackTrace();return null;}}
}

逻辑删除

  • 逻辑删除的本质是修改操作,所谓的逻辑删除其实并不是真正的删除,而是在表中将对应的是否删除标识,做修改操作。比如: 0是未删除,1是删除。在逻辑上数据是被删除的,但数据本身依然存在库中。

物理删除

  • 物理删除就是真正的从数据库中做删除操作了。
Service层编写

修改CourseService 添加 findCourseList 方法

/*** 课程管理模块 Service层 接口*/
public interface CourseService {public List<Course> findCourseList();}/*** 课程管理模块 Service层实现类*/
public class CourseServiceImpl implements CourseService {// 创建CourseDaoCourseDao courseDao = new CourseDaoImpl();@Overridepublic List<Course> findCourseList() {//调用Dao 进行查询List<Course> courseList = courseDao.findCourseList();return courseList;}
}
Servlet编写
接口开发规范

​ 我们在做的是一个前后端分离项目、需要通过接口文档对接的项目。所以开发过程中要仔细查看前端所需的api接口和参数字段

为了严格按照接口进行开发,提高效率,对请求及响应格式进行规范化。

开发规范
2、post请求时有三种数据格式,可以提交form表单数据 和 Json数据(ContentType=application/json),文件等多部件类型(multipart/form-data)三种数据格式 . jsonl类型的数据 Servlet中使用 fastjson进行解析
3、响应结果统一格式为json
开发规范
1、get 请求时,采用key/value格式请求,Servlet中可以使用 getParameter() 获取。
2、post请求时有三种数据格式
第一种: Json数据,jsonl类型的数据 Servlet中使用 fastjson进行解析
第二种: 提交form表单数据
第三种: 文件等多部件类型(multipart/form-data)
3、响应结果统一格式为json

为什么使用JSON?

​ 数据格式简单,易于读写,JSON格式能够直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量,但是完成的任务不变,且易于维护

本项目使用的是 JSON解析工具为阿里巴巴的fastjson,maven工程导入下面的依赖即可

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.1.37</version>
</dependency><dependency><groupId>com.colobu</groupId><artifactId>fastjson-jaxrs-json-provider</artifactId><version>0.3.1</version>
</dependency>
接口文档

​ 前端的开发基于服务端编写的接口,如果前端人员等待服务端人员将接口开发完毕再去开发前端内容这样做效率是 非常低下的,所以当接口定义完成,可以使用工具生成接口文档,前端人员查看接口文档即可进行前端开发,这样 前端和服务人员并行开发,大大提高了生产效率。

编写CourseServlet

在CourseServlet中添加 findCourseList方法

@WebServlet("/course")
public class CourseServlet extends BaseServlet {// 查询课程信息列表public void findCourseList(HttpServletRequest request, HttpServletResponse response){try {// 1.接收参数// 2.业务处理CourseServiceImpl cs = new CourseServiceImpl();List<Course> courseList = cs.findCourseList();// 3.响应结果// SimplePropertyPreFilter指定要转换的JSON字段SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,"id","course","price","sort_num","status");String result = JSON.toJSONString(courseList,filter);response.getWriter().print(result);} catch (IOException e) {e.printStackTrace();}}
}

Postman

postMan介绍

Postman是一款功能强大的http接口测试工具,使用postman可以完成http各种请求的功能测试。

官方地址:https://www.getpostman.com/

安装Postman

本教程使用,双击打开 Postman-win64-6.0.10-Setup.exe

Postman使用

1、新建一个Postman窗口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lOGMWQVm-1625924522849)(E:\MarkDown\拉勾笔记\新建Postman窗口)]

2、窗口介绍

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pCbJfedk-1625924522850)(E:\MarkDown\拉勾笔记\Postman窗口介绍)]

使用postman测试接口
  1. 发送请求到指定的
http://localhost:8080/lagou_edu_home/course?methodName=findCourseList

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-leHEV11E-1625924522852)(E:\MarkDown\拉勾笔记\使用postman测试接口)]

创建模块将请求分类
  1. 创建课程模块

  2. 选择 Save As 将请求保存到对应模块中

  3. 描述一下请求的相关信息

功能二:多条件查询课程信息

需求分析

  1. 根据课程名称和课程状态进行查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AjcMfepT-1625924522854)(E:\MarkDown\拉勾笔记\根据课程名称和课程状态进行查询)]

  1. 要查询的字段
id,  course_name,price,  sort_num,   STATUS
  1. 查询条件
is_del
course_name
status

根据条件查询课程信息

Dao层编写
  1. 因为是多条件查询,所以要注意多个参数情况下,SQL的编写
接口// 根据课程名称,课程状态 查询课程信息public List<Course> findByCourseNameAndStatus(String courseName, String status);实现类/*** 根据课程名称,课程状态 查询课程信息* *///根据条件查询课程信息@Overridepublic List<Course> findByCourseNameAndStatus(String courseName, String status) {try {//1.创建QueryRunnerQueryRunner 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 转换为 intint 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;}}
Service层编写
CourseService 接口public List<Course> findByCourseNameAndStatus(String courseName, String status);CourseServiceImpl 实现类@Overridepublic List<Course> findByCourseNameAndStatus(String course_name, String status) {List<Course> courseList = courseDao.findByCourseNameAndStatus(course_name, status);return courseList;}
Servlet编写

在CourseServlet中添加 findByCourseNameOrStatus方法

// 根据条件查询课程信息
public void findByCourseNameAndStatus(HttpServletRequest request,HttpServletResponse response){try {// 1.接收参数String course_name = request.getParameter("course_name");String status = request.getParameter("status");// 2.业务处理CourseService cs = new CourseServiceImpl();List<Course> courseList = cs.findByCourseNameAndStatus(course_name, status);// 3.返回结果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();}
}
接口测试

请查阅接口文档,使用postman进行接口测试

功能三:新建课程营销信息

需求分析

  1. 选择新建课程,对课程营销信息进行录入
基本信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gkIG9l53-1625924522856)(E:\MarkDown\拉勾笔记\课程营销信息基本信息)]
销售信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hvjlgMqL-1625924522858)(E:\MarkDown\拉勾笔记\课程营销信息销售信息)]

分享信息

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CTZJD7th-1625924522859)(E:\MarkDown\拉勾笔记\课程营销信息分享信息)]

课程详情

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZM9DpZUo-1625924522861)(E:\MarkDown\拉勾笔记\课程信息)]

Dao层编写

接口// 保存课程营销信息public int saveCourseSalsesInfo(Course course);实现类// 保存课程营销信息@Overridepublic int saveCourseSalsesInfo(Course course) {try {// 1.创建QueryRunnerQueryRunner qr = new QueryRunner(DruidUtils.getDataSource());// 2.编写SQLString 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()};// 执行插入操作int row = qr.update(sql, param);return row;} catch (SQLException e) {e.printStackTrace();return 0;}}

Dao层方法测试

// 测试保存课程营销信息
@Test
public void testsaveCourseSalesInfo(){// 1.创建Course对象Course course = new Course();course.setCourse_name("爱情36计");course.setBrief("学会去找对象");course.setTeacher_name("药水哥");course.setTeacher_info("人人都是药水哥");course.setPreview_first_field("共10讲");course.setPreview_second_field("每周日更新");course.setDiscounts(88.88);course.setPrice(188.0);course.setPrice_tag("最新优惠价");course.setShare_image_title("哈哈哈");course.setShare_title("嘻嘻嘻");course.setShare_description("天天向上");course.setCourse_description("爱情36计,就像一场游戏");course.setCourse_img_url("https://www.xx.com/xxx.jpg");course.setStatus(1);    // 1 上架 0 下架String formart = DateUtils.getDateFormart();course.setCreate_time(formart);course.setUpdate_time(formart);int i = courseDao.saveCourseSalesInfo(course);System.out.println(i);
}

Service层编写

1.编写枚举类,设置响应状态码

public enum StatusCode {SUCCESS(0,"success"),FAIL(1,"fail");// 定义属性private int code;private String message;StatusCode() {}StatusCode(int code, String message) {this.code = code;this.message = message;}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@Overridepublic String toString() {JSONObject object = new JSONObject();object.put("status",code);object.put("msg",message);return object.toString();}
}

2.编写Service

接口public String saveCourseSalesInfo(Course course);实现类@Overridepublic String saveCourseSalesInfo(Course course) {// 1.补全课程营销信息String strDate = DateUtils.getDateFormart();course.setCreate_time(strDate);course.setUpdate_time(strDate);course.setStatus(1);// 2.执行插入操作int row = courseDao.saveCourseSalesInfo(course);// 3.判断if (row > 0){// 插入成功String result = StatusCode.SUCCESS.toString();return result;} else {String result = StatusCode.FAIL.toString();return result;}}

文件上传

图片上传分析

在添加课程营销信息的表单中,有一个图片上传项

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KdpXuI7b-1625924522863)(E:\MarkDown\拉勾笔记\课程营销信息图片上传)]

文件上传介绍

文件上传的实质:文件的拷贝

  • 文件上传:从本地将文件拷贝到服务器磁盘上

    • 客户端: 需要编写文件上传表单
    • 服务端: 需要编写代码接受上传的 文件
客户端编码
  1. 文件上传三要素:
  • 1.表单提交方式: post (get方式提交有大小限制,post没有)
  • 2.表单的enctype属性:必须设置为 multipart/form-data
    • enctype就是encodetype就是编码类型的意思
    • multipart/form-data是多部件文件上传 , 指表单数据有多部分构成,既有文本数据,又有文件等二进制数据的意思
  • 3.表单必须有文件上传项: **file **,必须要有name属性和值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1qwrLthb-1625924522864)(E:\MarkDown\拉勾笔记\营课程销信息客户端编码)]

注意:  默认情况下,表单的enctype的值是application/x-www-form-urlencoded,不能用于文件上传,只有使用了multipart/form-data,才能完整的传递文件数据
  1. 代码示例
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head><title>Title</title>
</head>
<body><%--文件上传三要素1.表单提交方式必须是 post2.表单的 enctype属性必须是:multipart/form-data3.表单中必须有文件上传项--%><form method="post" enctype="multipart/form-data"><input type="file" name="upload"><br/><input type="text" name="name"><input type="text" name="password"><input type="submit" value="文件上传"></form>
</body>
</html>
服务端编码

服务端要接收文件上传的表单数据

上传文件, 抓包分析

使用360浏览器进行抓包,谷歌浏览器不方便查看

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Txohe1tq-1625924522866)(E:\MarkDown\拉勾笔记\服务端编码上传文件, 抓包分析)]

服务端获上传的文件
  1. 通过request获取请求体的内容

  2. 解析请求体 多部件上传的特点是,每个input都是一个表单项

    根据分隔符将请求中所有的内容,切割成数组,数组中的每一个元素 都是一个表单项

  3. 遍历数组,分清楚那个是普通的表单项,哪个是 文件上传项

    如何区分?判断是否有 filename

  4. 获取到普通表单项中的内容,通过属性name获取

  5. 获取文件上传项内容

    文件名: filname = aaa.txt

    文件内容:

  6. 使用IO将文件内容,保存到服务器中

FileUpload工具类
导入依赖

FileUpload包可以很容易地将文件上传到你的Web应用程序

IOUtils封装了Java中io的常见操作,使用十分方便,需要下载 commons-io-1.4.jar 包

<dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>1.4</version>
</dependency><dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.2.1</version>
</dependency>
FileUpload 核心类介绍
类名 介绍
DiskFileItemFactory 磁盘文件项工厂,读取文件时相关的配置,比如: 缓存的大小,临时目录的位置
ServletFileUplaod 文件上传的一个核心类
FileItem 代表每一个表单项
文件上传的API的详解

1、ServletFileUpload

方法 说明
isMultipartContent(request); 判断是否是一个文件上传的表单
parseRequest(request); 解析request获得表单项的集合
setHeaderEncoding(“UTF-8”); 设置上传的文件名的编码方式

2、FileItem

方法 说明
isFormField() 判断是否是普通表单项
getFieldName() 获得表单的name属性值
item.getString() 获得表单的value值
getName() 获得上传文件的名称
getInputStream() 获得上传文件
delete() 删除临时文件
文件上传后台代码编写

FileUpload使用步骤:

​ 1、创建磁盘文件项工厂

​ 2、创建文件上传的核心类

​ 3、解析request—获得文件项集合

​ 4、遍历文件项集合

​ 5、判断普通表单项/文件上传项

@WebServlet("/upload")
public class FileUploadServlet extends HttpServlet {@Overrideprotected 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){// 普通表单项String fieldName = item.getFieldName();String fieldValue = item.getString("utf-8");    // 设置编码System.out.println(fieldName +" = " +fieldValue);}else {// 文件上传项// 获取文件名String fileName = item.getName();// 拼接新的文件名 使用UUID保证不重复String newFileName = UUIDUtils.getUUID() +"" + fileName;// 获取输入流InputStream in = item.getInputStream();// 创建输出流FileOutputStream out = new FileOutputStream("G:/upload/" + newFileName);// 使用IOUtils完成 文件的copyIOUtils.copy(in,out);// 关闭流out.close();in.close();}}}}} catch (FileUploadException e) {e.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
将图片上传到tomcat服务器
将项目部署到webapps

将部署方式改变为 war模式,把项目部署在tomcat的webapps下

  • idea中部署项目两种方式

    • war模式:将项目以war包的形式上传真实到服务器的webapps目录中;
    • war exploded模式:仅仅是目录的映射,就相当于tomcat在项目源文件夹中启动一样;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yTdYsz7I-1625924522868)(E:\MarkDown\拉勾笔记\将项目部署到webapps)]

在webapps中创建upload目录

upload目录专门用来保存上传过来的图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-drthooo3-1625924522870)(E:\MarkDown\拉勾笔记\在webapps中创建upload目录)]

修改代码,将图片上传到服务器

修改图片的输出路径

  1. 获取到项目的运行目录信息
  2. 截取到webapps的 目录路径
  3. 拼接输出路径,将图片保存到upload
@WebServlet("/upload")
public class FileUploadServlet extends HttpServlet {@Overrideprotected 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){// 普通表单项String fieldName = item.getFieldName();String fieldValue = item.getString("utf-8");    // 设置编码System.out.println(fieldName +" = " +fieldValue);}else {// 文件上传项// 获取文件名String fileName = item.getName();// 拼接新的文件名 使用UUID保证不重复String newFileName = UUIDUtils.getUUID() +"" + fileName;// 获取输入流InputStream in = item.getInputStream();// 创建输出流// 1.获取项目运行目录  D:\Tomcat\apache-tomcat-8.5.55\webapps\lagou_edu_home\String realPath = this.getServletContext().getRealPath("/");// 2.截取到 webapps 目录路径String webappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));// 3.拼接输出路径,将图片保存到 uploadFileOutputStream out = new FileOutputStream(webappsPath+"/upload/" + newFileName);// 使用IOUtils完成 文件的copyIOUtils.copy(in,out);// 关闭流out.close();in.close();}}}}} catch (FileUploadException e) {e.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
页面加载图片

将tomcat作为图片服务器使用时,存储上传的图片后,如果想要图片可以访问,需要在idea中进行配置:

  1. 选择external source —> 找到webapps目录下的的upload文件夹

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hajPQe99-1625924522871)(E:\MarkDown\拉勾笔记\页面加载图片)]

  1. 上传一张图片到服务器

  2. 在项目内部页面加载图片

<img src="/upload/b17a85f7c53b4ce5856d79044c16216b天空.jpg">
  1. 也可以通过HTTP方式访问
http://localhost:8080/upload/b17a85f7c53b4ce5856d79044c16216b天空.jpg

BeanUtils工具类

  1. 介绍

​ BeanUtils 是 Apache commons组件的成员之一,主要用于简化JavaBean封装数据的操作。可以将一个表单提交的所有数据封装到JavaBean中

  1. 导入依赖
<dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>1.8.3</version>
</dependency>
  1. BeanUtils 对象常用方法
方法 描述
populate(Object bean, Map properties) 将Map数据封装到指定Javabean中,一般用于将表单的所有数据封装到javabean
setProperty(Object obj,String name,Object value) 设置属性值
getProperty(Object obj,String name) 获得属性值
  1. BeanUtils 使用测试
public class TestBeanUtils {@Testpublic void test01() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {// 1.创建Course对象Course course = new Course();// 2.创建MapHashMap<String, Object> map = new HashMap<>();// 3.向Map集合中添加数据,key要与course的属性名保持一致,value的数据类型与course的属性类型保持一致map.put("id",1);map.put("course_name","大数据");map.put("brief","课程包含所有大数据流行的技术");map.put("teacher_name","周星星");map.put("teacher_info","非著名演员");// 将map中的数据封装到 course中BeanUtils.populate(course,map);System.out.println(course.getId()+" " + course.getCourse_name() +" " + course.getBrief()+" " +course.getTeacher_name()+" " +course.getTeacher_info());// 设置属性 获取属性BeanUtils.setProperty(course,"price",100.0);String price = BeanUtils.getProperty(course, "price");System.out.println(price);}
}

Servlet编写

CourseSalesInfoServlet

创建CourseSalesInfoServlet类,继承HttpServlet , 完成保存课程营销信息操作

因为上传的信息包含文件信息,无法直接通过request直接获取参数,所以不能继承BaseServlet

@WebServlet("/courseSalesInfo")
public class CourseSalesInfoServlet extends HttpServlet {/*** 保存课程营销信息*      收集表单数据,封装到course对象中,将图片上传到Tomcat服务器中*/@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {// 1.创建Course对象Course course = new Course();// 2.创建Map集合,用来收集数据HashMap<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 webappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));// 创建输出流FileOutputStream out = new FileOutputStream(webappsPath + "/upload/" + newFileName);// 使用IOUtils完成 文件的copyIOUtils.copy(in,out);// 关闭流out.close();in.close();// 将图片路径进行保存map.put("course_img_url","/upload/" + newFileName);}}// 使用BeanUtils 将map中的数据封装到course对象中BeanUtils.populate(course,map);// 补全信息String dateFormart = DateUtils.getDateFormart();course.setCreate_time(dateFormart); // 创建时间course.setUpdate_time(dateFormart); // 修改时间course.setStatus(1);    // 上架// 业务处理CourseServiceImpl cs = new CourseServiceImpl();String result = cs.saveCourseSalesInfo(course);// 响应结果resp.getWriter().print(result);} catch (Exception e) {e.printStackTrace();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}
接口测试

postman测试上传文件

  1. 接口地址填写正确
  2. 将请求方式设置为POST
  3. 需要上传文件, 设置Headers: “key”:“Content-Type”, “value”:"multipart/form-data"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9YjkFXwc-1625924522873)(E:\MarkDown\拉勾笔记\接口测试)]

  1. Body选择form-data

  2. key 右侧下拉选择file;value 点击Select Files选择文件 , 按照接口文档,补全测试参数

保存图片URL优化

1.创建常量类

public final class Constants {//本地访问地址public static final String LOCAL_URL = "http://localhost:8080";
}

2.拼接图片URL

 //将图片路径进行保存map.put("course_img_url", Constants.LOCAL_URL+"/upload/" + newFileName);

功能四:修改营销课程信息

需求分析

​ 营销信息其实就是课程相关的信息,操作的依然是 course 表。我们通过点击营销信息按钮,进入到对应的课程营销信息页面,对原有信息进行修改。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M7WEOhC4-1625924522876)(E:\MarkDown\拉勾笔记\修改课程营销信息需求分析)]

Dao层编写

  1. 通过上面的分析,首先要编写 根据课程ID查询课程信息,进行回显
接口//根据课程ID 查询课程信息public Course findCourseById(int id);实现类//根据课程ID 查询课程营销信息@Overridepublic Course findCourseById(int id) {try {QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());String sql ="SELECT\n" +"id,\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" +"FROM course WHERE id = ?";Course course = qr.query(sql, new BeanHandler<Course>(Course.class), id);return course;} catch (SQLException e) {e.printStackTrace();return null;}}
-- 根据ID查询课程信息SQL
SELECT
id,
course_name,
brief,
teacher_name,
teacher_info,
preview_first_field,
preview_second_field,
discounts,
price,
price_tag,
course_img_url,
share_image_title,
share_title,
share_description,
course_description,
STATUS
FROM course WHERE id = ?;
  1. 编写修改课程营销信息的方法,将修改写入数据库
接口//修改课程营销信息public int updateCourseSalesInfo(Course course);实现类//修改课程营销信息@Overridepublic int updateCourseSalesInfo(Course course) {try {QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());String sql = "UPDATE course SET\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" +"update_time = ?\n" +"WHERE id = ?;";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.getUpdate_time(),course.getId()};int row = qr.update(sql, param);return row;} catch (SQLException e) {e.printStackTrace();return 0;}}
修改课程
UPDATE course SET
course_name = ?,
brief = ?,
teacher_name = ?,
teacher_info = ?,
preview_first_field = ?,
preview_second_field = ?,
discounts = ?,
price = ?,
price_tag = ?,
share_image_title = ?,
share_title = ?,
share_description = ?,
course_description = ?,
course_img_url = ?,
update_time = ?
WHERE id = ?
  1. 测试

Service层编写

接口public Course findCourseById(int id);实现类@Overridepublic Course findCourseById(int id) {return courseDao.findCourseById(id);}
接口public String updateCourseSalesInfo(Course course);实现类@Overridepublic String updateCourseSalesInfo(Course course)  {//调用daoint i = courseDao.updateCourseSalesInfo(course);//根据插入是否成功,封装对应信息if(i > 0){//保存成功String result = StatusCode.SUCCESS.toString();return result;}else{//保存失败String result = StatusCode.FAIL.toString();return result;}}

Servlet编写

根据ID查询课程信息

1、CourseServlet

在CourseServlet中,添加根据ID查询课程信息的功能

// 根据课程ID 查询课程信息
public void findCourseById(HttpServletRequest request,HttpServletResponse response){try {// 1.接收参数String id = request.getParameter("id");// 2.业务处理CourseService cs = new CourseServiceImpl();Course course = cs.findCourseById(Integer.parseInt(id));// 3.返回结果//使用 SimplePropertyPreFilter,指定要转换为JSON的字段SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,"id","course_name","brief","teacher_name","teacher_info","preview_first_field","preview_second_field","discounts","price","price_tag","share_image_title","share_title","share_description","course_description");String result = JSON.toJSONString(course,filter);response.getWriter().print(result);} catch (IOException e) {e.printStackTrace();}
}

2、接口测试

详见接口文档

修改CourseSalesInfoServlet

1、需求分析

保存营销信息和修改营销信息,访问的是同一个接口,所以在CourseSalesInfoServlet中,我们需要进行一下判断

  • 携带id 就是修改操作
  • 未携带id就是新增操作

2、代码修改

@WebServlet("/courseSalesInfo")
public class CourseSalesInfoServlet extends HttpServlet {/*** 保存课程营销信息*      收集表单数据,封装到course对象中,将图片上传到Tomcat服务器中*/@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {try {// 1.创建Course对象Course course = new Course();// 2.创建Map集合,用来收集数据HashMap<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 webappsPath = realPath.substring(0, realPath.indexOf("lagou_edu_home"));// 创建输出流FileOutputStream out = new FileOutputStream(webappsPath + "/upload/" + newFileName);// 使用IOUtils完成 文件的copyIOUtils.copy(in,out);// 关闭流out.close();in.close();// 将图片路径进行保存map.put("course_img_url", Constants.LOCAL_URL+ "/upload/" + newFileName);}}// 使用BeanUtils 将map中的数据封装到course对象中BeanUtils.populate(course,map);String dateFormart = DateUtils.getDateFormart();// 业务处理CourseServiceImpl cs = new CourseServiceImpl();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();}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}

3、接口测试

根据接口文档,进行测试

功能五:修改课程状态

需求分析

  1. 数据库中课程状态码为0或者1,课程状态,0-草稿(下架),1-上架

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aRhEe99P-1625924522878)(E:\MarkDown\拉勾笔记\修改课程状态需求分析)]

  1. 页面分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hOE68EOD-1625924522880)(E:\MarkDown\拉勾笔记\修改课程状态 页面分析)]

DAO层编写

接口//修改课程状态int updateCourseStatus(Course course);实现类//修改课程状态@Overridepublic int updateCourseStatus(Course course) {try {QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());String sql = "UPDATE course SET STATUS = ? ,update_time = ? WHERE id = ?";Object[] param = {course.getStatus(),course.getUpdate_time(),course.getId()};int row = qr.update(sql, param);return row;} catch (SQLException e) {e.printStackTrace();return 0;}}

Service层编写

接口public Map<String,Integer> updateCourseStatus(Course course);实现类@Overridepublic Map<String, Integer> updateCourseStatus(Course course) {// 1.调用daoint row = courseDao.updateCourseStatus(course);Map<String,Integer> map = new HashMap<>();// 判断课程状态if (row > 0){if (course.getStatus() == 0){map.put("status",0);}else {map.put("status",1);}}return map;}

Servlet编写

在CourseServlet中,添加updateCourseStatus方法

// 修改课程状态
public void updateCourseStatus(HttpServletRequest request,HttpServletResponse response){try {// 1.获取参数String id = request.getParameter("id");// 2.业务处理CourseService cs = new CourseServiceImpl();// 3.根据课程ID 查询课程信息Course course = cs.findCourseById(Integer.parseInt(id));// 4.判断课程信息状态int status = course.getStatus();if (status == 0){// 如果是0 设置为1course.setStatus(1);}else {// 如果是1 设置为0course.setStatus(0);}// 5.设置更新时间course.setUpdate_time(DateUtils.getDateFormart());// 6.修改状态Map<String, Integer> map = cs.updateCourseStatus(course);// 7.响应结果String result = JSON.toJSONString(map);response.getWriter().print(result);} catch (IOException e) {e.printStackTrace();}}

接口测试

查看接口文档,进行测试

课程管理内容模块开发

开发流程

需求分析

配置课时(课程内容管理)模块,主要是对课程内容进行管理

点击“配置课时” ----> Servlet:根据课程ID查询出对应的章节信息和章节对应的课时信息。需要查询三张表:课时表、章节表、课时表

数据库表分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jgxrdxX3-1625924522881)(E:\MarkDown\拉勾笔记\拉勾教育管理系统数据库表关系)]

实体类设计

  1. Course类与 Course_Section类是一对多关系

​ 在Course类中定义一个List集合,并指定List的泛型是Course_Section类型,表示一个课程中可以包含多个章节

Course类//添加list集合 泛型是Course_SectionList<Course_Section> sectionList = new ArrayList<>();

​ 在 Course_Section类中,定义一个Course类型的属性,用来保存章节所对应的具体的课程信息

Course_Section类//添加一个Course类型的属性private Course course;
  1. Course_Section类与Course_Lesson类是一对多关系

​ 在Course_Section类中定义一个List集合,并指定List的泛型是Course_Lesson类型,这样就可以表示一个章节中包含多个课时

Course_Section类//添加一个list集合 泛型是Course_lessonList<Course_Lesson> lessonList = new ArrayList<>();Course_Lesson类//添加一个Course_Section类型的属性private Course_Section course_section;

Dao接口及实现类编写

/**
*课程内容管理DAO层接口
**/
public interface CourseContentDao{}/**
*课程内容管理DAO层实现类
**/
public class CourseContentDaoImpl implements CourseContentDao{}

Service接口及实现类编写

/**
*课程内容管理Service层接口**/
public interface CourseContentService{}/**
*课程内容管理Service层实现类
**/
public class CourseContentServiceImpl implements CourseContentService{}

CourseContentServlet 编写

CourseContentServlet 继承 BaseServlet

@WebServlet("/courseContent")
public class CourseContentServlet extends BaseServlet{}

功能一: 展示课程内容

需求分析

分析: 要展示的内容是对应课程下的章节与课时信息

  1. 我们先写一条查询语句: 查询ID为1 的课程的章节与课时信息
SELECTcscs.id,   -- 章节idcs.section_name, -- 章节名称cl.id, -- 课时idcl.theme -- 课时描述
FROM course_section cs INNER JOIN course_lessoncl
ON cs.id=cl.section_idWHEREcs.course_id=?
  1. 我们在程序中尽量避免使用连接查询,我们可以将上面的SQL进行拆分,每一条SQL对应一个功能
--根据课程ID查询章节相关的内容
SELECTid,course_id,section_name,description,order_num,from course_section cs where course_id = ?;--根据章节ID查询课时相关的内容
SELECTid,course_id,section_id,theme,duration,is_free,order_nu
FROM course_lesson WHERE section_id = ?;

DAO层编写

编写两个方法:

接口// 根据课程ID查询课程相关信息public List<Course_Section> findSectionAndLessonByCourseId(int courseId);// 根据章节ID 查询章节相关的课时信息public List<Course_Lesson> findLessonBySectionId(int sectionId);实现类// 根据课程ID查询课程相关信息@Overridepublic List<Course_Section> findSectionAndLessonByCourseId(int courseId) {try {// 1.创建QueryRunnerQueryRunner qr = new QueryRunner(DruidUtils.getDataSource());// 2.编写SQLString 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) {// 调用 根据章节ID查询课时信息方法// 获取章节对应的课时List<Course_Lesson> lessonList = findLessonBySectionId(section.getId());// 将课时数据封装到 章节对象中section.setLessonList(lessonList);}return sectionList;} catch (SQLException e) {e.printStackTrace();return null;}}// 根据章节ID查询课时信息@Overridepublic 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;}}

DAO层测试

public class TestCourseContentDao {CourseContentDao contentDao = new CourseContentDaoImpl();// 测试 查询对课程下的章节与课时@Testpublic void testFindSectionAndLessonByCourseId(){List<Course_Section> list = contentDao.findSectionAndLessonByCourseId(59);for (Course_Section courseSection : list) {System.out.println(courseSection.getId()+" = "+courseSection.getSection_name());List<Course_Lesson> lessonList = courseSection.getLessonList();for (Course_Lesson lesson : lessonList) {System.out.println(lesson.getId()+ " = "+lesson.getTheme()+ " = "+lesson.getSection_id());}}}}

Service层编写

接口
/*** 课程内容管理Service层接口*/
public interface CourseContentService {// 根据课程id查询课程内容public List<Course_Section> findSectionAndLessonByCourseId(int courseId);}实现类
/*** 课程内容管理Service层实现类*/
public class CourseContentServiceImpl implements CourseContentService {CourseContentDao contentDao = new CourseContentDaoImpl();@Overridepublic List<Course_Section> findSectionAndLessonByCourseId(int courseId) {List<Course_Section> sections = contentDao.findSectionAndLessonByCourseId(courseId);return sections;}
}

Servlet编写

CourseContentServlet中添加findSectionAndLessonByCourseId方法

@WebServlet("/courseContent")
public class CourseContentServlet extends BaseServlet {// 展示对应课程的章节与课时信息public void findSectionAndLessonByCourseId(HttpServletRequest request, HttpServletResponse response){try {// 1.获取参数String course_id = request.getParameter("course_id");// 2.业务处理CourseContentService contentService = new CourseContentServiceImpl();List<Course_Section> list = contentService.findSectionAndLessonByCourseId(Integer.parseInt(course_id));// 3.返回结果String result = JSON.toJSONString(list);response.getWriter().print(result);} catch (IOException e) {e.printStackTrace();}}}

接口测试

查看接口文档,进行测试

功能二: 新建章节信息

需求分析

点击 ”添加章节“(根据课程ID 查询课程名称) ----> Servlet:根据课程ID 查询课程信息进行回显 ----> 章节信息 ----> 点击”保存“ ----> Servlet:保存章节数据

DAO层编写

接口// 根据课程ID 回显课程信息public Course findCourseByCourseId(int courseId);// 保存章节信息public int saveSection(Course_Section section); 实现类// 根据课程ID 回显课程信息@Overridepublic Course findCourseByCourseId(int courseId) {try {// 1.创建QueryRunnerQueryRunner qr = new QueryRunner(DruidUtils.getDataSource());// 2.编写SQLString sql = "SELECT id,course_name FROM course WHERE id = ?";// 3.执行查询Course course = qr.query(sql, new BeanHandler<Course>(Course.class), courseId);return course;} catch (Exception e) {e.printStackTrace();return null;}}// 保存章节信息@Overridepublic int saveSection(Course_Section section) {try {QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());String sql = "INSERT INTO course_section(\n" +"course_id,\n" +"section_name,\n" +"description,\n" +"order_num,\n" +"STATUS,\n" +"create_time,\n" +"update_time\n" +") VALUES (?,?,?,?,?,?,?)";Object[] param = {section.getCourse_id(),section.getSection_name(),section.getDescription(),section.getOrder_num(),section.getStatus(),section.getCreate_time(),section.getUpdate_time()};int row = qr.update(sql, param);return row;} catch (Exception e) {e.printStackTrace();return 0;}}

Service层编写

接口// 根据id查询课程信息public Course findCourseByCourseId(int courseId);// 保存章节信息public String saveSection(Course_Section section);实现类@Overridepublic Course findCourseByCourseId(int courseId) {Course course = contentDao.findCourseByCourseId(courseId);return course;}@Overridepublic String saveSection(Course_Section section) {// 1.补全章节信息section.setStatus(2);   // 0:隐藏, 1:待更新, 2:已发布String formart = DateUtils.getDateFormart();section.setCreate_time(formart);section.setUpdate_time(formart);// 2.调用daoint row = contentDao.saveSection(section);// 3.根据是否插入成功,封装对应信息if (row > 0){// 保存成功String result = StatusCode.SUCCESS.toString();return result;}else {// 保存失败String result = StatusCode.FAIL.toString();return result;}}

Servlet层编写

CourseContentServlet中添加findCourseById方法

课程信息回显接口
// 根据课程id 回显课程信息
public void findCourseById(HttpServletRequest request,HttpServletResponse response){try {// 1.获取参数String course_id = request.getParameter("course_id");// 2.业务处理CourseContentService contentService = new CourseContentServiceImpl();Course course = contentService.findCourseByCourseId(Integer.parseInt(course_id));// 3.返回JSON数据SimplePropertyPreFilter filter = new SimplePropertyPreFilter(Course.class,"id","course_name");String result = JSON.toJSONString(course, filter);response.getWriter().print(result);} catch (IOException e) {e.printStackTrace();}
}
保存章节信息接口
1. POST请求方式介绍

POST 请求方法常用的三种数据提交格式

格式 说明
Content-Type : application/x-www-form-urlencoded 请求体中的数据会以普通表单形式(键值对)发送到后端
Content-Type : application/json ; charset=utf-8 请求体中的数据会以json字符串的形式发送到后端
Content-Type : multipart/form-data 多部件上传既可以上传键值对也可以上传文件

注意: 第二种JSON格式与第三种多部件上传,使用 getParameter() 方法都无法获取数据

2.需求分析分析

根据接口文档描述:前台传输的是JSON格式的数据,使用getParameter() 方法无法获取参数

{"methodName":"saveOrUpdateSection","course_id":19,"section_name:"微服务架构","description":"跟着药水一起学习如何使用微服务","order_num":0
}
3. 修改BaseServlet

如果请求参数是JSON格式的数,我们可以通过request.getReader()这个方法,获取一个流对象来进行读取

  1. 在BaseServlet中创建一个方法,用来获取JSON格式的数据
/*** POST请求格式为:   application/json ; charset=utf-8* 使用该方法进行读取*/
public String getPostJSON(HttpServletRequest request){try {// 1.从request中 获取缓冲输入流对象BufferedReader reader = request.getReader();// 2.创建StringBuffer 保存读取出的数据StringBuffer sb = new StringBuffer();// 3.循环读取String line = null;while((line = reader.readLine()) != null){// 将每次读取的数据 追加到StringBuffersb.append(line);}// 4.返回结果return sb.toString();} catch (IOException e) {e.printStackTrace();return null;}
}
  1. 修改BaseServlet中的doGet方法

1.获取POST请求的 Content-Type类型

2.判断传递的数据是不是JSON格式

3.如果是就调用上面编写的 getPostJSON方法,获取数据

4.将获取到的JSON格式的字符串转换为 Map

5.从Map中获取要调用的方法名

6.将Map保存到request域对象中 (流只能使用一次)

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {/*// 1.获取参数 要访问的方法String methodName = req.getParameter("methodName");*/String methodName = null;// 1.获取POST请求的 Content-Type类型String contentType = req.getHeader("Content_Type");// 2.判断传递的数据是不是JSON格式if ("application/json ; charset=utf-8".equals(contentType)){// 是JSON格式 调用getPostJSON()String postJSON = getPostJSON(req);// 将JSON格式的字符串转换为mapMap<String,Object> map = JSON.parseObject(postJSON, Map.class);// 从map集合中获取methodNamemethodName = (String)map.get("methodName");// 将获取到的数据,保存到request域对象中req.setAttribute("map",map);}else {methodName = req.getParameter("methodName");}// 2.判断 执行对应的方法if (methodName != null){// 通过反射优化代码 提升代码的可维护性try {// 1.获取字节码文件对象  this = TestServletClass c = this.getClass();// 2.根据传入的方法名,获取对应的方法对象Method method = c.getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class);// 3.调用method对象的 invoke方法,执行对应的功能method.invoke(this,req,resp);} catch (Exception e) {e.printStackTrace();System.out.println("请求的功能不存在!");}}
}
4. 编写接口代码
/*** 保存&修改 章节信息*/
public void saveOrUpdateSection(HttpServletRequest request,HttpServletResponse response){try {// 1.获取参数 从域对象中获取Map<String,Object> map = (Map)request.getAttribute("map");// 2.创建章节对象Course_SectionCourse_Section section = new Course_Section();// 3.使用BeanUtils工具类,将map中的数据封装到 sectionBeanUtils.populate(section,map);// 4.业务处理CourseContentService contentService = new CourseContentServiceImpl();String result = contentService.saveSection(section);// 5.响应结果response.getWriter().print(result);} catch (Exception e) {e.printStackTrace();}
}
5. 测试接口
  1. 选择POST请求方式,设置Content-Type = application/json

  2. 选择raw 发送JSON格式数据

功能三: 章节信息修改

需求分析

选择“章节”,点击编辑,需要先查询对应章节的信息(章节id)----> Servlet:Servlet中根据章节id查询对应章节信息,并返回 ----> 点击保存 ----> Servlet:根据章节id 修改章节信息。

注意: 接口文档中并没有要求编写回显接口,说明回显操作由前端完成

DAO层编写

接口// 修改章节public int updateSection(Course_Section section);实现类// 修改章节信息@Overridepublic int updateSection(Course_Section section) {try {QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());String sql = "UPDATE course_section SET\n" +"section_name = ?,\n" +"description = ?,\n" +"order_num = ?,\n" +"update_time = ? WHERE id = ?";Object[] param = {section.getSection_name(),section.getDescription(),section.getOrder_num(),section.getUpdate_time(),section.getId()};int row = qr.update(sql, param);return row;} catch (SQLException e) {e.printStackTrace();return 0;}}

Service层编写

接口// 修改章节public String updateSection(Course_Section section);实现类@Overridepublic String updateSection(Course_Section section) {// 1.补全信息String date = DateUtils.getDateFormart();section.setUpdate_time(date);// 2.调用daoint row = contentDao.updateSection(section);// 3.判断是否修改成功if (row > 0){// 修改成功String result = StatusCode.SUCCESS.toString();return  result;}else {// 失败String result = StatusCode.FAIL.toString();return result;}}

Servlet编写

保存章节信息和修改章节信息,访问同一个接口,所以在saveOrUpdateSection方法中中,我们要进行判断

​ 携带id 就是修改章节操作

​ 未携带id就是新增章节操作

/*** 保存&修改 章节信息*/
public void saveOrUpdateSection(HttpServletRequest request,HttpServletResponse response){try {// 1.获取参数 从域对象中获取Map<String,Object> map = (Map)request.getAttribute("map");// 2.创建章节对象Course_SectionCourse_Section section = new Course_Section();// 3.使用BeanUtils工具类,将map中的数据封装到 sectionBeanUtils.populate(section,map);// 4.业务处理CourseContentService contentService = new CourseContentServiceImpl();// 判断是否携带IDif (section.getId() == 0){// 新增章节操作String result = contentService.saveSection(section);// 5.响应结果response.getWriter().print(result);}else {// 修改章节信息操作String result = contentService.updateSection(section);response.getWriter().print(result);}} catch (Exception e) {e.printStackTrace();}
}

接口测试

查看接口文档,进行测试

功能四: 章节状态管理

需求分析

根据选择的状态信息,发送对应的状态编号进行修改,status 状态,0:隐藏;1:待更新;2:已发布

DAO层编写

接口// 修改章节状态public int updateSectionStatus(int id,int status); 实现类// 修改章节状态@Overridepublic int updateSectionStatus(int id, int status) {try {QueryRunner qr = new QueryRunner(DruidUtils.getDataSource());String sql = "UPDATE course_section SET STATUS = ?,update_time = ? WHERE id = ?";Object[] param = {status,DateUtils.getDateFormart(),id};int row = qr.update(sql, param);return row;} catch (SQLException e) {e.printStackTrace();return 0;}}

Service层编写

接口// 修改章节状态public String updateSectionStatus(int id,int status);实现类@Overridepublic String updateSectionStatus(int id, int status) {int row = contentDao.updateSectionStatus(id, status);// 判断是否修改成功if (row > 0){//修改状态成功String result = StatusCode.SUCCESS.toString();return result;}else {// 修改失败String result = StatusCode.FAIL.toString();return result;}}

Servlet编写

/*** 修改章节状态*/
public void updateSectionStatus(HttpServletRequest request,HttpServletResponse response){try {// 1.获取参数int id = Integer.parseInt(request.getParameter("id"));  // 章节idint status = Integer.parseInt(request.getParameter("status"));  // 章节状态// 2.业务处理CourseContentServiceImpl contentService = new CourseContentServiceImpl();String result = contentService.updateSectionStatus(id, status);// 3.返回结果response.getWriter().print(result);} catch (Exception e) {e.printStackTrace();}
}

接口测试

查看接口文档,进行测试

小结

1、项目概述

​ 1)项目简介——拉勾教育后台管理系统——课程管理模块

​ 2)后台项目主要模块:课程管理、课程内容管理

​ 3)项目架构:前后端分离架构

2、开发流程

​ 1)分析产品需求

​ 2)编写接口文档

​ 3)按照接口文档进行开发

3、代码编写

​ 1)项目搭建

​ 2)基础代码准备

​ 3)功能开发:

​ a).dao层开发

​ b).dao层测试

​ c).service层开发

​ d).servlet接口开发

​ e).接口测试

Ajax

ajax概述

​ 传统网页如果需要更新内容,必须重载整个网页页面。每当向服务器发送请求,哪怕只更新局部内容,服务器都会将整个页面进行刷新。这种方式缺点是:

​ 1、性能会有所降低

​ 2、用户的操作页面会中断

1)什么是Ajax

Ajax即“Asynchronous Javascript And XML”,指一种创建交互式网页应用的网页开发技术

Ajax = 异步 JavaScript 和 XML

Ajax是客户端与服务器进行交互时,可以【不必刷新整个浏览器】的情况下,与服务器进行异步通讯的技术

2)Ajax的作用

​ Ajax 可以使网页实现异步更新。这意味着可以在不重新加载真个网页的情况下,对网页的某部分进行更新(局部更新)

3)Ajax的好处

1、减轻服务器负担,按需要获得数据

2、无刷新更新页面,减少用户的实际和心理等待时间

3、只更新部分页面,有效利用带宽

3、主流浏览器都支持Ajax

4)异步与同步

浏览器访问服务器的方式

​ 同步访问:客户端必须等待服务器响应,在等待过程中不能进行其他操作

​ 异步访问:客户端不需要等待服务器响应,等待期间,可以进行其他操作

JS方式ajax的实现(了解)

JS的ajax:出现最早。使用一个对象XmlHttpRequest对象。专门用于进行ajax请求发送,和响应的接收。使用ajax发送请求,使用ajax接收响应,使用JS进行页面刷新。

缺点:

​ 若使用JS的AJAX技术,为了实现简单功能,就需要书写大量复杂代码

​ JS的AJAX代码,浏览器兼容性比较差

前端JS代码

login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head><meta http-equiv="content-type" content="text/html;charset=UTF-8"><title>new jsp</title><script>// 原生JS方式发送 Ajax请求function run() {// 1.核心对象var xmlhttp;// 判断浏览器类型if (window.XMLHttpRequest){// code for IE7+, Firefox, Chrome, Opera, Safarixmlhttp=new XMLHttpRequest();}else{// code for IE6, IE5xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}// 3.建立连接xmlhttp.open("GET","/login?username=tom",true);   /*method,url,async:true(异步)或 false(同步)*/// 4.发送请求xmlhttp.send();// 5.获取响应结果xmlhttp.onreadystatechange=function() {// readyState==4  请求已完成,且响应已就绪if (xmlhttp.readyState==4 && xmlhttp.status==200){var text =xmlhttp.responseText;alert("相应结果:" + text);}}}</script>
</head>
<body><input type="button" value="原生JS发送异步请求" οnclick="run()"><br>局部刷新 <input type="text">
</body>
</html>
@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1.获取请求数据String username = req.getParameter("username");try {// 模拟后台处理过程Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// 2.进行响应System.out.println(username);resp.getWriter().print(username);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}

jQuery框架的ajax

JQuery框架的ajax简介

jquery是一个优秀的js框架,自然对js原生的ajax进行了封装,封装后的ajax的操作方法更简洁,功能更强大

与ajax操作相关的jquery方法有如下几种:POST GET AJAX

GET请求方式

通过远程HTTP GET请求载入信息。这是一个简单的GET请求功能,如需复杂的参数设置请使用$.ajax

GET请求方式语法

$.get(url,data,callback,type)

参数1:url 请求路径

参数2:data 请求时携带的数据

​ 格式:key=value 或者 {username=‘baby’,pwd:666}

参数3:callback 响应成功后的回调函数

参数4:type 响应的数据类型 text html xml json

// JQuery GET方式
function run2() {// 1.urlvar url = "/login";// 2.数据var data = {username:"jack"};// 3.发送GET请求$.get(url,data,function (param) {alert("GET异步请求 响应成功: " + param);},"text")
}

POST请求方式

通过远程HTTP POST请求载入信息。这是一个简单的POST请求功能,如需复杂的参数设置请使用$.ajax

POST请求方式语法

​ $.post{url,data,callback,type}

里面的四个参数和get方式是一样的,不一样的数请求方式的不同

 // JQuery POST方式
function run3() {// 1.urlvar url = "/login";// 2.数据var data = {username:"lucy"};// 3.发送POST请求$.post(url,data,function (param) {alert("POST异步请求 响应成功: " + param);},"text")
}

Ajax请求方式

$.ajax()方法可以更加详细的设置底层参数。该方法通常用于其他方法不能完成的请求。

ajax请求的语法

方式1:jQuery.ajax({[settings]})

方式2:$.ajax({})

settings是一个js字面量形式的对象,格式是键值对{name:value,name:value},常用属性名

// JQuery Ajax方式
function run4() {$.ajax({url:"/login",async:true, // 是否异步data:{username:"giao"},type:"POST",    // 请求方式dataType:"text", // 返回数据的数据类型success:function (param) {alert("响应成功!" + param);},error:function (param) {alert("响应失败!");}});
}

案例:检测用户名是否已经被注册

需求:用户输入用户名,鼠标移除后,对用户名进行判断,提示用户名是否可用

步骤:

1.准备Servlet,对用户名进行校验,并返回结果(是否可用)

2.为页面输入框,绑定鼠标移除时间

3.进行异步请求,获取响应结果

4.根据结果,动态添加HTML代码

后台Servlet

@WebServlet("/checkName")
public class CheckNameServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 设置编码req.setCharacterEncoding("utf-8");resp.setContentType("text/html;charset=utf-8");// 获取用户名String username = req.getParameter("username");// 封装数据HashMap<String, Object> map = new HashMap<>();// 判断用户是否存在if ("tom".equals(username)){// 用户存在map.put("flag",true);map.put("msg","用户名已经被占用!");// 响应JSON格式的数据String data = JSON.toJSONString(map);resp.getWriter().print(data);}else {// 用户名不存在map.put("flag",false);map.put("msg","用户名可以使用!");// 响应JSON格式的数据String data = JSON.toJSONString(map);resp.getWriter().print(data);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {this.doGet(req, resp);}
}

前台jsp页面

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head><meta http-equiv="content-type" content="text/html; charset=utf-8"><title>new jsp</title><script typet="text/javascript" src="http://code.jquery.com/jquery-latest.js"></script><script>$(function () {$("username").blur(function () {// 获取用户名var name = $(this).val();// 判断用户名不为空和空格串if(name != null && name != ""){// 向后台发送请求,验证用户名$.ajax({url:"/checkName",type:"GET",data:{username:name},dataType:"json",success:function (data) {if(data.flag){// 设置span的内容体$("#spanMsg").html("<font color='red'>" + data.msg + "<font>");}else if(!data.flag){$("#spanMsg").html("<font color='green'>" + data.msg + "<font>");}},error:function () {alert("请求处理失败!");}});}})});</script></head>
<body><form action="#" method="post">用户名: <input type="text" id="username" name="username" placeholder="请输入用户名"><span id="spanMsg" style="color: red"></span><br>密码: <input type="text" id="password" name="password" placeholder="请输入密码"></form>
</body>
</html>

拉勾教育管理系统(后端)相关推荐

  1. 拉勾教育管理系统(下)【笔记】

    文章目录 项目上线部署发布 1.前言 1.1.服务器与操作系统 1.2.项目的发布部署 2.后台项目部署 2.1.安装虚拟机 2.2.搭建开发环境 2.2.1.安装xsheel和Xftp工具 2.2. ...

  2. 拉勾教育管理系统(上)【笔记】

    文章目录 1.需求分析 1.1.课程管理 1.2.营销信息 (营销信息其实就是课程的具体信息) 1.3.配置课时(配置课时指的就是对课程内容的配置,课程内容就包括了 章节信息和课时信息) 2.课程管理 ...

  3. [附源码]计算机毕业设计Python拉勾教育课程管理系统(程序+源码+LW文档)

    该项目含有源码.文档.程序.数据库.配套开发软件.软件安装教程 项目运行 环境配置: Pychram社区版+ python3.7.7 + Mysql5.7 + HBuilderX+list pip+N ...

  4. 拉勾教育后台管理系统(SSM)(课程管理模块开发)【学习笔记】

    文章目录 1.项目架构 1.1.项目介绍 1.2.技术选型 1.2.1.前端技术选型 1.2.2.后端技术选型 1.3.项目开发环境 2.Maven进阶使用(Maven聚合工程) 2.1.maven的 ...

  5. 06_04_任务一:拉勾教育后台管理系统[课程管理模块、图片上传、 BeanUtils封装实体类](SSM)

    拉勾教育后台管理系统(SSM) 1. 项目架构 1.1 项目介绍 ​ 拉勾教育后台管理系统,是提供给拉勾教育的相关业务人员使用的一个后台管理系统, 业务人员可以在 这个后台管理系统中,对课程信息.广告 ...

  6. 06_04_SSM拉勾教育后台管理系统(权限模块\登录及动态菜单)

    拉勾教育后台管理系统(SSM)权限模块 权限概念介绍 权限:权利(能做的)和限制(不能做的),在权限范围内做好自己的事情,不该看的不看,不该做的不做 认证: 验证用户名密码是否正确的过程 授权: 对用 ...

  7. 06_04_任务二:SSM拉勾教育后台管理系统(广告模块与用户模块)

    拉勾教育后台管理系统(SSM) 广告模块 广告模块功能分析 拉勾教育后台管理系统的 广告管理模块包含了以下功能: 广告位列表查询 添加&修改广告位 回显广告位名称 广告分页查询 图片上传接口 ...

  8. 拉勾教育-求职技巧总结

    求职技巧 文章目录 求职技巧 把握投递简历的黄金时间段 读懂职位 JD,精准投递简历 做好充分的准备去面试 电话沟通 如何全面的介绍自己 内容 关键 工作离职原因 职业规划 工作中更看重的是什么 还有 ...

  9. 08_01_分布式架构搭建拉勾教育PC站

    分布式架构搭建拉勾教育PC站 1. 项目架构 1.1 项目介绍 拉勾教育PC站,是提供给我们学员观看技术视频的网站.学员使用手机号注册登录后,可以选择适合自己的课程,并观看课程视频,当然,有免费的课程 ...

最新文章

  1. 怎么修剪_夹竹桃怎么养?这样“修剪”一下,一年开3、4次花,花大色艳
  2. 【每周CV论文】初学深度学习图像超分辨应该要读的文章
  3. The superclass javax.servlet.http.HttpServlet was not found 问题解决
  4. 低代码,填补业务技术鸿沟 or 紧贴业务的开发时代?
  5. Mac 10.12下安装python3环境
  6. 百搭电商背景素材,设计师应急PSD分层模板
  7. 创业之路这样走 必死无疑
  8. Android UI开发第四十一篇——墨迹天气3.0引导界面及动画实现
  9. 黑客们成功地在土豆上安装了Linux!
  10. Microsoft Dynamics AX 2012
  11. Win10在使用setuna2时,启动截屏后屏幕会放大的问题
  12. 什么是servlet技术?如何部署和新建一个servlet?
  13. 探寻微博背后的大数据原理:微博推荐算法简述
  14. 超好用的mac虚拟机软件:VM虚拟机 mac中文版
  15. 达摩院提出时序预测新模型 有效提升预测精准度
  16. dnf剑魂buff等级上限_DNF:剑魂最强武器,比星之海伤害更高,无神话红10都能8000亿...
  17. javascript学习网站
  18. Git 修改commit提交信息
  19. 2020年汽车驾驶员(高级)免费试题及汽车驾驶员(高级)实操考试视频
  20. NMF 非负矩阵分解(Non-negative Matrix Factorization)实践

热门文章

  1. 如何解决数据关联查询
  2. 防电脑辐射的方法,电脑防辐射技巧
  3. 人脸识别门禁为何倍受青睐?
  4. 录音文件转换成文字的方法
  5. Arrays.sort与Arrays.parallelSort区别
  6. Map相关、HashMap
  7. 自我鉴定范文 php,毕业生优秀自我鉴定
  8. 图像处理--边缘检测
  9. docker 入门 —— docker 镜像命令
  10. 【C语言】memcmp、memcpy、memmove、memset、memchr函数详解