SpringBootWeb案例-Tlias智能学习辅助系统

YangeIT大约 32 分钟SpringBootPageHelper前后端协议SpringBootJSON

SpringBootWeb案例-Tlias智能学习辅助系统

今日目标

🎯 将前端开发(已经提供)、后端开发、数据库整合起来

项目原型效果:

点击查看项目原型效果open in new window

点击查看api接口文档

完成后的成品效果展示:image-20220904103734643

今日主要内容

  • 准备工作(导入数据库、创建工程) ✏️ 🍐
  • 部门管理
    • 部门查询 ✏️
    • 删除部门 ✏️
    • 新增部门 ✏️
  • 员工管理
    • 分页查询 ✏️
    • 分页查询(含条件) ✏️ ❤️
    • 删除员工 ✏️

知识储备

  1. 了解Web前端开发的基础知识(了解异步请求、get和post请求方式)
  2. Web后端开发的基础(HTTP协议(特点)、请求响应(组成))
  3. 数据库MySQL,以及通过Mybatis框架如何来完成数据库的基本操作

如果不熟悉Mybatis点击查看SpringBoot整合Mybatis入门案例

1. 准备工作 🚩

准备工作

1、部门管理

image-20221213205503102
image-20221213205503102

部门管理功能开发包括:

  • 查询部门列表
  • 删除部门
  • 新增部门
  • 修改部门

环境搭建 代码操作👇

核心步骤

image-20221213230710821
image-20221213230710821
  1. 准备数据库表(dept、emp)
  2. 创建springboot工程,引入对应的起步依赖(web、mybatis、mysql驱动、lombok)
  3. 配置文件application.properties中引入mybatis的配置信息,准备对应的实体类
  4. 准备对应的Mapper、Service(接口、实现类)、Controller基础结构

第1步:准备数据库表

-- 部门管理
create table dept(
    id int unsigned primary key auto_increment comment '主键ID',
    name varchar(10) not null unique comment '部门名称',
    create_time datetime not null comment '创建时间',
    update_time datetime not null comment '修改时间'
) comment '部门表';
-- 部门表测试数据
insert into dept (id, name, create_time, update_time) values(1,'学工部',now(),now()),(2,'教研部',now(),now()),(3,'咨询部',now(),now()), (4,'就业部',now(),now()),(5,'人事部',now(),now());



-- 员工管理(带约束)
create table emp (
  id int unsigned primary key auto_increment comment 'ID',
  username varchar(20) not null unique comment '用户名',
  password varchar(32) default '123456' comment '密码',
  name varchar(10) not null comment '姓名',
  gender tinyint unsigned not null comment '性别, 说明: 1 男, 2 女',
  image varchar(300) comment '图像',
  job tinyint unsigned comment '职位, 说明: 1 班主任,2 讲师, 3 学工主管, 4 教研主管, 5 咨询师',
  entrydate date comment '入职时间',
  dept_id int unsigned comment '部门ID',
  create_time datetime not null comment '创建时间',
  update_time datetime not null comment '修改时间'
) comment '员工表';
-- 员工表测试数据
INSERT INTO emp
	(id, username, password, name, gender, image, job, entrydate,dept_id, create_time, update_time) VALUES
	(1,'jinyong','123456','金庸',1,'1.jpg',4,'2000-01-01',2,now(),now()),
	(2,'zhangwuji','123456','张无忌',1,'2.jpg',2,'2015-01-01',2,now(),now()),
	(3,'yangxiao','123456','杨逍',1,'3.jpg',2,'2008-05-01',2,now(),now()),
	(4,'weiyixiao','123456','韦一笑',1,'4.jpg',2,'2007-01-01',2,now(),now()),
	(5,'changyuchun','123456','常遇春',1,'5.jpg',2,'2012-12-05',2,now(),now()),
	(6,'xiaozhao','123456','小昭',2,'6.jpg',3,'2013-09-05',1,now(),now()),
	(7,'jixiaofu','123456','纪晓芙',2,'7.jpg',1,'2005-08-01',1,now(),now()),
	(8,'zhouzhiruo','123456','周芷若',2,'8.jpg',1,'2014-11-09',1,now(),now()),
	(9,'dingminjun','123456','丁敏君',2,'9.jpg',1,'2011-03-11',1,now(),now()),
	(10,'zhaomin','123456','赵敏',2,'10.jpg',1,'2013-09-05',1,now(),now()),
	(11,'luzhangke','123456','鹿杖客',1,'11.jpg',5,'2007-02-01',3,now(),now()),
	(12,'hebiweng','123456','鹤笔翁',1,'12.jpg',5,'2008-08-18',3,now(),now()),
	(13,'fangdongbai','123456','方东白',1,'13.jpg',5,'2012-11-01',3,now(),now()),
	(14,'zhangsanfeng','123456','张三丰',1,'14.jpg',2,'2002-08-01',2,now(),now()),
	(15,'yulianzhou','123456','俞莲舟',1,'15.jpg',2,'2011-05-01',2,now(),now()),
	(16,'songyuanqiao','123456','宋远桥',1,'16.jpg',2,'2007-01-01',2,now(),now()),
	(17,'chenyouliang','123456','陈友谅',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());

完成后项目工程结构image-20221213224927868

总结

SpringBoot集成Mybatis步骤:

  1. 创建Spring Boot项目
  2. 添加依赖
  3. 配置数据源
  4. 创建实体类
  5. 创建Mapper接口,书写注解SQL或者Mapper配置文件
  6. 编写Service层,编写接口和实现类
  7. 编写Controller层,按照接口规范编写方法
  8. 运行应用
  9. 编写单元测试

课堂作业

  1. Mapper接口上只要添加了@Mapper注解就可以创建代理对象? 🎤 1.如果不添加@Mapper注解,可以创建代理对象吗? 🎤

1.2 开发规范 🍐

开发规范

开发规范一

开发规范一:REST风格的URL中有4种请求方式,分别代表:增删改查

案例是基于当前最为主流进行开发。

image-20221213230911102
image-20221213230911102

在前后端分离的开发模式中,前后端开发人员都需要根据提前定义好的接口文档,来进行前后端功能的开发。

后端开发人员:必须严格遵守提供的接口文档进行后端功能开发(保障开发的功能可以和前端对接)

image-20221213231519551
image-20221213231519551

点击查看api接口文档

在前后端进行交互的时候,我们需要基于当前主流的REST风格的API接口进行交互。

REST(Representational State Transfer),表述性状态转换,它是一种软件架构风格。 ❤️ 👇 👇

REST风格详情

传统URL风格如下:

http://localhost:8080/user/getById?id=1     GET:查询id为1的用户
http://localhost:8080/user/saveUser         POST:新增用户
http://localhost:8080/user/updateUser       POST:修改用户
http://localhost:8080/user/deleteUser?id=1  GET:删除id为1的用户

我们看到,原始的传统URL呢,定义比较复杂,而且将资源的访问行为对外暴露出来了。

基于REST风格URL如下:

http://localhost:8080/users/1  GET:查询id为1的用户
http://localhost:8080/users    POST:新增用户
http://localhost:8080/users    PUT:修改用户
http://localhost:8080/users/1  DELETE:删除id为1的用户

其中总结起来,就一句话:

在REST风格的URL中,通过四种请求方式,来操作数据的增删改查。

  • GET : 查询
  • POST :新增
  • PUT :修改
  • DELETE :删除

我们看到如果是基于REST风格,定义URL,URL将会更加简洁、更加规范、更加优雅。

注意事项:

  • REST是风格,是约定方式,约定不是规定,可以打破
  • 描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。如:users、emps、books…

开发规范二

开发规范二:统一响应结果

前后端工程在进行交互时,使用统一响应结果 Result。

111
111

Result代码

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
    private Integer code;//响应码,1 代表成功; 0 代表失败
    private String msg;  //响应信息 描述字符串
    private Object data; //返回的数据

    //增删改 成功响应
    public static Result success(){
        return new Result(1,"success",null);
    }
    //查询 成功响应
    public static Result success(Object data){
        return new Result(1,"success",data);
    }
    //失败响应
    public static Result error(String msg){
        return new Result(0,msg,null);
    }
}

开发规范三:开发流程

我们在进行功能开发时,都是根据如下流程进行:

image-20220904125004138
image-20220904125004138

步骤详情:

  1. 查看页面原型明确需求

  2. 阅读接口文档 点击查看api接口文档

  3. 思路分析

  4. 功能接口开发

    • 就是开发后台的业务功能,一个业务功能,我们称为一个接口
  5. 功能接口测试

    • 功能开发完毕后,先通过Postman进行功能接口测试,测试通过后,再和前端进行联调测试
  6. 前后端联调测试

    • 和前端开发人员开发好的前端工程一起测试 🎉

总结

  • 开发规范一:REST风格的URL中有4种请求方式,分别代表:增删改查
  • 开发规范二:统一响应结果
  • 开发规范三: 我们在进行功能开发时,都是根据如下流程进行:
image-20220904125004138
image-20220904125004138

课堂作业

  1. 阅读接口文档能获取哪些信息?🎤
  2. 接口测试的好处是什么?🎤

2. 部门管理 🚩

开发的部门管理功能包含:

  1. 查询部门
  2. 删除部门
  3. 新增部门
  4. 更新部门(不讲解,自己独立完成)🚩

1️⃣2.1 查询部门 ✏️

需求:查询部门列表,不需要分页

原型截图
原型截图

查询的部门的信息:部门ID、部门名称、修改时间

通过页面原型以及需求描述-->部门查询,是不需要考虑分页操作的。

点击查看项目原型效果open in new window

功能开发 👇 👇

思路步骤
思路步骤

DeptController

@Slf4j
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;

    //@RequestMapping(value = "/depts" , method = RequestMethod.GET)
    @GetMapping("/depts")
    public Result list(){
        log.info("查询所有部门数据");
        List<Dept> deptList = deptService.list();
        return Result.success(deptList);
    }
}

@Slf4j注解源码:

image-20221214000909044
image-20221214000909044

2️⃣2.2 前后端联调🚀

前后端联调(部署前端项目)

完成了查询部门的功能,我们也通过postman工具测试通过了,下面我们再基于前后端分离的方式进行接口联调。

image-20221214101436198
image-20221214101436198

部署步骤:

1、将资料中提供的"前端环境"文件夹中的压缩包,拷贝到一个没有中文不带空格的目录下

image-20221214100230484
image-20221214100230484

总结

  1. vue打包后的包,可以运行在nginx服务器中,使用反向代理访问后台资源
  2. 前端通过ajax发起异步请求获取后端数据

课堂作业

  1. 为什么使用Nginx部署前端资源,有和好处?🎤
  2. 后台端口是8080,为何前端访问使用90端口?🎤
  3. 完成查询部门案例并且完成前后端联调案例,体会前后端开发的流程

3️⃣2.3 删除部门 ✏️

删除部门

点击部门列表后面操作栏的 "删除" 按钮,就可以删除该部门信息。 此时,前端只需要给服务端传递一个ID参数就可以了。 我们从接口文档中也可以看得出来。

功能开发 👇 👇

image-20221214102705490
image-20221214102705490

DeptController

@Slf4j
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;

    @DeleteMapping("/depts/{id}")
    public Result delete(@PathVariable Integer id) {
        //日志记录
        log.info("根据id删除部门");
        //调用service层功能
        deptService.delete(id);
        //响应
        return Result.success();
    }
    
    //省略...
}



 
 

 
 



 






总结

  1. 删除部门操作本质执行的是sql:delete from dept where id = ?

课堂作业

接口文档规定:

前端请求路径:/depts/{id}
前端请求方式:DELETE
  • 问题1:怎么在controller中接收请求路径中的路径参数?

  • 问题2:如何限定请求方式是delete?

4️⃣ 2.4 新增部门 ✏️

新增部门

点击 "新增部门" 按钮,弹出新增部门对话框,输入部门名称,点击 "保存" ,将部门信息保存到数据库。

功能开发 👇 👇

image-20221214115519648
image-20221214115519648

DeptController

@Slf4j
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;

    @PostMapping("/depts")
    public Result add(@RequestBody Dept dept){
        //记录日志
        log.info("新增部门:{}",dept);
        //调用service层添加功能
        deptService.add(dept);
        //响应
        return Result.success();
    }

    //省略...
}

总结

  1. 新增数据使用json传递参数,封装实体类接受json参数,且属性名和参数key一样,
  2. 使用@RequestBody修饰实体类,自动实现数据注入
  3. 插入数据使用Post请求方式

课堂作业

  1. 请问如果插入的参数很多,使用注解不是很方便,有其他解决方案吗?🎤
  2. 如何限定请求方式是POST? 🎤
  3. 怎么在controller中接收json格式的请求参数?🎤

5️⃣2.5 请求路径优化 🍐

请求路径优化

首先我们先来看下目前controller层代码:

image-20221215110553435
image-20221215110553435

以上三个方法上的请求路径,存在一个共同点:都是以/depts作为开头。(重复了)

总结

  1. @RequestMapping注解可以修饰方法也可以修饰类,可以将公共的路径前缀,提取到类上,精简代码。

课堂作业

  1. 🚩 根据接口文档的需求,独自完成部门修改。部门修改接口文档

注意:修改是发起Put请求,如果F12查看Network 发现是Post请求 ,说明没有完成回显接口

3. 员工管理 🚩

开发员工管理功能

image-20221215142107329
image-20221215142107329

基于以上原型,我们可以把员工管理功能分为:

  1. 分页查询(今天完成 🎯
  2. 带条件的分页查询(今天完成)🎯
  3. 删除员工(今天完成)🎯
  4. 新增员工(后续完成)
  5. 修改员工(后续完成)

3.1 分页查询 ✏️

前言

我们之前做的查询功能,是将数据库中所有的数据查询出来并展示到页面上,试想如果数据库中的数据有很多(假设有十几万条)的时候,将数据全部展示出来肯定不现实,那如何解决这个问题呢?

使用分页解决这个问题。每次只展示一页的数据,比如:一页展示10条数据,如果还想看其他的数据,可以通过点击页码进行查询。

image-20221215141233541
image-20221215141233541

要想从数据库中进行分页查询,我们要使用LIMIT关键字,格式为:limit 开始索引 每页显示的条数

limit起始索引算法:

查询第1页数据的SQL语句是:

select * from emp  limit 0,10;

查询第2页数据的SQL语句是:

select * from emp  limit 10,10;

查询第3页的数据的SQL语句是:

select * from emp  limit 20,10;

观察以上SQL语句,发现: 开始索引一直在改变 , 每页显示条数是固定的

开始索引的计算公式: 开始索引 = (当前页码 - 1) * 每页显示条数

功能开发: 👇 👇

EmpController


@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {

    @Autowired
    private EmpService empService;

    //条件分页查询
    @GetMapping
    public Result page(@RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize) {
        //记录日志
        log.info("分页查询,参数:{},{}", page, pageSize);
        //调用业务层分页查询功能
        PageBean pageBean = empService.page(page, pageSize);
        //响应
        return Result.success(pageBean);
    }
}

@RequestParam(defaultValue="默认值") //设置请求参数默认值

总结

课堂作业

  1. 分页需求中,sql的关键字是什么? 🎤
  2. 分页关键字的第一个参数的算法是什么? 🎤

3.2 分页插件 🍐 ❤️

分页插件

PageHelper是Mybatis的一款功能强大、方便易用的分页插件,支持任何形式的单标、多表的分页查询。

官网:https://pagehelper.github.io/open in new window

image-20221215170038833
image-20221215170038833

在执行empMapper.list()方法时,就是执行:select * from emp 语句,怎么能够实现分页操作呢?

分页插件帮我们完成了以下操作:

  1. 先获取到要执行的SQL语句:select * from emp
  2. 把SQL语句中的字段列表,变为:count(*)
  3. 执行SQL语句:select count(*) from emp //获取到总记录数
  4. 再对要执行的SQL语句:select * from emp 进行改造,在末尾添加 limit ? , ?
  5. 执行改造后的SQL语句:select * from emp limit ? , ?

代码实现

步骤提示

  1. 当使用了PageHelper分页插件进行分页,就无需再Mapper中进行手动分页了。
  2. 在Mapper中我们只需要进行正常的列表查询即可,无需在sql语句中插入分页参数

在Service层中,调用Mapper的方法之前设置分页参数,在调用Mapper方法执行查询之后,解析分页结果,并将结果封装到PageBean对象中返回。

1、在pom.xml引入依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper-spring-boot-starter</artifactId>
    <version>1.4.2</version>
</dependency>

总结

课堂作业

  1. 前端需要传递给后端什么数据?🎤
  2. 返回给前端的分页数据,怎么封装?🎤

3.3 分页查询(带条件) ✏️ 🍐 ❤️

分页查询(带条件)

需求:在分页查询的基础上,添加条件,进行查询

image-20221215175639974
image-20221215175639974

通过员工管理的页面原型我们可以看到,员工列表页面的查询,不仅仅需要考虑分页,还需要考虑查询条件。 分页查询我们已经实现了,接下来,我们需要考虑在分页查询的基础上,再加上查询条件。

我们看到页面原型及需求中描述,搜索栏的搜索条件有三个,分别是:

  • 姓名:模糊匹配
  • 性别:精确匹配
  • 入职日期:范围匹配
select * 
from emp
where 
  name like concat('%','张','%')   -- 条件1:根据姓名模糊匹配
  and gender = 1                   -- 条件2:根据性别精确匹配
  and entrydate = between '2000-01-01' and '2010-01-01'  -- 条件3:根据入职日期范围匹配
order by update_time desc;

而且上述的三个条件,都是可以传递,也可以不传递的,也就是动态的。 我们需要使用前面学习的Mybatis中的动态SQL 。

思路分析image-20221215180528415

在原有分页查询的代码基础上进行改造:

EmpController

@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {

    @Autowired
    private EmpService empService;

    //条件分页查询
    @GetMapping
    public Result page(@RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize,
                       String name, Short gender,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end) {
        //记录日志
        log.info("分页查询,参数:{},{},{},{},{},{}", page, pageSize,name, gender, begin, end);
        //调用业务层分页查询功能
        PageBean pageBean = empService.page(page, pageSize, name, gender, begin, end);
        //响应
        return Result.success(pageBean);
    }
}

总结

课堂作业

  1. 带条件分页查询,条件之间使用or还是and🎤
  2. 独自完成分页查询以及PageHelper的插件的使用,并通过文字写出分页查询的流程和描述PageHelper插件的基本原理。

3.3 删除员工 ✏️

删除员工

新的功能:删除员工

image-20221215183657413
image-20221215183657413

当我们勾选列表前面的复选框,然后点击 "批量删除" 按钮,就可以将这一批次的员工信息删除掉了。也可以只勾选一个复选框,仅删除一个员工信息。

删除多个员工包含只删除一个员工

功能开发:

image-20221215184714815
image-20221215184714815

EmpController

@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {

    @Autowired
    private EmpService empService;

    //批量删除
    @DeleteMapping("/{ids}")
    public Result delete(@PathVariable List<Integer> ids){
        empService.delete(ids);
        return Result.success();
    }


}









 
 
 
 
 



总结

课堂作业

  1. 怎么在controller中接收请求路径中的路径参数?🎤
  2. 如何限定请求方式是delete?🎤
  3. 在Mapper接口中,执行delete操作的SQL语句时,条件中的id值是不确定的是动态的,怎么实现呢?🎤

课后作业

🚩 1. 重点完成上述的课堂作业

  1. 晚自习第一节课的前30分钟,总结完毕之后,每个同学先必须梳理今日知识点 (记得写不知道的,以及感恩三件事);整理好的笔记可以发给组长,组长交给班长,意在培养大家总结的能力)

  2. 晚自习第一节课的后30分钟开始练习(记住:程序员是代码堆起来的):

    • 先要把今天的所有案例或者课堂练习,如果没练完的,练完他
    • 独立书写今日作业 Day10作业👈
  3. 剩余的时间:预习第二天的知识,预习的时候一定要注意:

  • 预习不是学习,不要死看第二天的视频(很容易出现看了白看,为了看视频而看视频)
  • 预习看第二天的笔记,把笔记中标注重要的知识,可以找到预习视频,先看一遍,如果不懂的 ,记住做好标注。