综合案例《tlias 智能学习辅助系统》
1 准备工作
1.1 数据库建表
1.2 项目结构
1.3 pom.XML 配置依赖
1.4 application.properties 配置
包括数据库,camel,日志
1.5 接口文档
1.6 (番外篇)springboot 三层架构
只有理解了三层架构才能更好地开发
springboot 的三层架构:
- controller 层
- service 层
- dao 层
1.6.1 controller 层
controller 层叫控制器层
负责前后端交互,接受前端请求,调用 service 层,接收 service 层返回的数据,最后返回具体的页面和数据到客户端
1.6.2 service 层
service 层叫业务逻辑层
存放业务逻辑处理,不直接对数据库进行操作,有接口和接口实现类,提供 controller 层调用的方法
创建两个文件,一个存放接口类,一个存放接口实现类
1.6.3 dao 层
dao 层也叫 mapper 层,数据访问层
对数据库进行持久化操作,他的方法是针对数据库操作的,基本用到的就是增删改查。它只是个接口,只有方法名字,具体实现在 mapper.xml 中
1.6.4 model 层
model 层也叫 pojo 层或者 entity 层,一般数据库的一张表对应一个pojo层,并且表中所有字段都在pojo层都一一对应
2 开发规范
2.1 RESTful
RESTful(REpresentational State Transfer),表述性状态转换,一种软件架构风格
HTTP动词:增、删、改、查
对应四种不同的请求:
- GET 查询
- POST 新增
- PUT 修改
- DELETE 删除
描述模块的功能通常使用复数,也就是加 s 的格式来描述,表示此类资源,而非单个资源。如:users、emps、books...
2.2 统一响应结果
把 Result 类放入 pojo 下
3 开发-部门管理(有一个待解决问题)
3.1 部门列表查询(有一个待解决问题)
3.1.1 查看接口文档
- 基本信息
- 请求参数
无
- 响应数据(Result 类格式)
3.1.2 思路
前端发来请求
controller 层进行接收,调用下一层的 service 层
service 层无法直接操作数据库,只有 dao层(数据访问层)才可以。所以 service 层继续
调用 dao 层
dao 层(mapper层)对数据库进行访问操作,然后返回
最后由 controller 层响应回前端
3.1.3 实现
在 debug 时,经常用到日志
private static Logger log = LoggerFactory.getLogger(DeptController.class);
log.info("查询全部部门数据");
其实可以用注解 @slf4j 简化:
在前面方法加上注解,就可以直接用 log 日志对象了
第一步:controller 层
Result pojo 对象是这样的:
对 success 方法进行了重载(overloading)
第二步,service 层
我们去 DeptService 接口,定义这个方法
然后我们再去 Impl 类文件里面去具体实现
由于 service 层还是无法直接操作数据库,我们接着往下调用去 dao 层(Mapper层),还是通过注入 dao 层对象调用这个对象的方法
第三步,dao 层(Mapper 层)
这里面只有接口(interface),直接写注解,写查询语句,剩下的交给 springboot
3.1.4 测试
运行入口文件,springboot 自动启动 tomcat,占用端口 8080
使用 postman 发送规定的 GET 请求:
得到数据结果
发送 POST 请求则报错
3.1.5 前后端联调(有一个待解决问题)
刚才的测试是用 tomcat 在后端测试,接下来的测试需要在前端发请求
这里有一个待解决的问题
使用自己写的 vue 前端页面,用 axios 做异步请求,能从
上面获取数据
但是从本机的
就不行,挂在 Nginx 服务器上显示“暂无数据”
每次进行访问,比如刷新页面和点击“部门管理”,在 IDEA 控制台都有响应
他们都是 JOSN 格式,区别在于 URL
已经尝试了很长时间,未果
于是,使用了课程资料的 nginx
里面自带了 dist(vue 脚手架打包之后的文件放在这里,启动 nginx,看到的页面就是这个)
居然能成功,怀疑是配置文件的问题,以后再说,因为现在根本看不懂这么复杂的代码
3.1.6 nginx 关闭的三种方法
-
nginx -s stop
-
nginx -s quit
-
taskkill /F /IM nginx.exe > nul
3.2 删除部门
3.2.1 查看文档
3.2.2 实现
测试:
成功删除
3.3 新增部门
3.3.1 查看文档
3.3.2 思路
注意 service 层要补充基础属性,因为前端发来 post 请求,里面只有参数 name,而我们要插入数据库的时候,要补充字段 create_time 和 update_time,这部分的操作在 service 层
3.3.3 实现
第一步,controller 层接受请求,并调用 service
使用注解是 @PostMapping 里面写文档要求的 depts
按照 POST 方式接收请求,是一个在 消息体 中的 JSON 文件,格式是:
{
"name":"就业部"
}
使用注解 @RequestBody
这样就把传递过来的 JSON 文件自动包装成一个 Dept 对象
第二步
比较经典的 service 接口、service 实现
要注意的是,在实现类里面,要补充字段
因为传递过来的 JSON 里面只有 name,而实际上 dept 数据库表有四个字段:id、name、create_time、update_time。
id 是设置成自增,不用管,但是还有两个时间字段,这时候就需要自己进行添加了,而之所以在 service 层添加,是因为 service 是 业务逻辑层 ,而下一层 dao 是 数据访问层,dao 直接进行操作而不是补充字段
第三步
(这里把右上角的数据库改了,现在不用加 tlias 前缀)
3.3.4 测试
3.4 controller 层注解简化
Mapping 既然都有公共字段,那就直接在前面抽取
3.5 (自己写)修改部门
没有演示,那就自己写吧
先阅读文档
请求参数样例:
{
"id": 1,
"name": "教研部"
}
第一步
在前面先加上
@RequestMapping("/depts")
同时要注意看文档,请求的方式是 PUT,之前我写成 POST ,和上面新增部门的方法请求方式相同了,springboot 报了错
第二步
第三步
之前我用的是
@Update("update dept set name = dept.name where id = dept.id")
这样并不能成功
原因是必须使用 mybatis 的动态 SQL,使用 #{} 的占位符,而上面写的,仅仅是字符串,相当于判断 id 为 'dept.id' 的字段,这显然是没有这个字段的
测试
4 开发-员工管理
员工管理更复杂,原因是需要 分页查询
4.1 基础分页查询
首先回顾 MySQL 分页查询
注意看下面,用户可以设置每页展示的数据条数,当前页码,还有一共多少条数据
4.1.1 文档
响应数据
{
"code": 1,
"msg": "success",
"data": {
"total": 2,
"rows": [
{
"id": 1,
"username": "jinyong",
"password": "123456",
"name": "金庸",
"gender": 1,
"image": "https://web-framework.oss-cnhangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg",
"job": 2,
"entrydate": "2015-01-01",
"deptId": 2,
"createTime": "2022-09-01T23:06:30",
"updateTime": "2022-09-02T00:29:04"
},
{
"id": 2,
"username": "zhangwuji",
"password": "123456",
"name": "张无忌",
"gender": 1,
"image": "https://web-framework.oss-cnhangzhou.aliyuncs.com/2022-09-02-00-27-53B.jpg",
"job": 2,
"entrydate": "2015-01-01",
"deptId": 2,
"createTime": "2022-09-01T23:06:30",
"updateTime": "2022-09-02T00:29:04"
}
]
}
}
4.1.2 思路
4.1.3 实现
上面提到了一个 实体类 PageBean,里面装的是关于分页的数据,把它放在 pojo 里面
字段的命名,都是根据 文档 来的
第一步,根据分析,在数据库需要两次查询,我们先写操作数据库的 dao 层
一个查总数据条数
一个查分页,分页查询返回的是 List<Emp>
第二步
文档要求要有默认值,我们可以加上 if 判断,如果为 null,我们就给值
但是这样太麻烦,所以我们使用注解
@Request(defaultValue = "默认值")
第三步
service 层,还是固定写法,但是注意的是公式
起始索引 = (页码数 - 1)* 一页的大小
4.1.4 测试
不输入分页信息,一次展示10条,起始索引 0(id 为1)
给定了固定的页码信息,我们要看第二页,从 11 开始到 17(因为一共17个)
4.2 PageHelper
引入 PageHelper 依赖
安装了 PageHelper 插件,我们现在只需要在 Mapper 里面定义一个最基本的查询
然后在 service 实现类中进行固定的操作:
- 第一步
设置分页参数(这是传过来的两个参数)
第一个参数是当前是第几页
第二个参数是一页几条数据
PageHelper.startPage(page,pageSize);
- 第二步
执行查询
拿到 emplist,要强制类型转换为 Page 类型,这是个已经定义好的方法
List<Emp> empList = empMapper.list(); Page<Emp> p = (Page<Emp>) empList;
然后就可以测试了
启动会有一个很好看的字符画
然后 postman
id 从 6 到 10,正好是第二页的 5 条
4.3 (自己写出来了)条件分页查询
4.3.1 分析
这是之前学过的 mybatis 的动态 SQL
先复习一下动态 SQL:
在和 EmpMapper 同包同名的 XML 文件里面写 SQL 语句,并且使用 mybatis 的 <if>标签,在里面进行姓名、性别、入职时间的三项任意动态查询
然后比较关键的是,不要在里面写 limit ,这是因为,我们要使用刚才用过的 PageHelper插件,这个插件可以基于普通查询自动分页,我们只需要调用接口就可以了
下面讨论的是各个方法之间的传参:
我们要展示的字段有以下这些
这些字段是从数据库能查到的,只要在 where 里面设置条件就可以。但是条件是从前端通过 GET 请求发过来的,这个请求含有name、gender、begin、end。这些参数需要从 controller 层去传到 dao (mapper)层,再加上我们要分页的两个参数分别是当前页码 page 和 一页数据量pageSize,一共是 6 个传参
4.3.2 实现
- 第一步 controller 层
在 page 和 pageSize 的基础上,又加了条件查询的 4 个参数
要注意的是,(看注释)我本来是自己手动去调整 begin 和 end ,一开始是 String 类型,但是,我们用的是 springboot,是一个【框架】,我们不需要自己去手动干活(我这样单纯地 parse 也不太好用,因为里面有一些空指针异常需要额外判断)。所以我们使用了注解
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin
这样是 springboot 框架自动处理,就不需要我们自己手动处理了
我们要面向注解编程
拿到所有参数,controller 层任务完成,调用下一层——service层
- 第二步 service 层
service 接口
我们使用了 PageHelper 插件,操作也是比较固定:
1.设置分页参数
PageHelper.startPage(page, pageSize);
这步要注意,不是 new 一个对象,是使用它内置的静态方法去设置,传递的参数就 page 和 pageSize
2.执行查询
List<Emp> empList = empMapper.selectCondition(name, gender, begin, end);
Page<Emp> p = (Page<Emp>) empList;
直接进行条件查询,我们要拿到的是一个基于传参的条件数据 List 表,把这个 List 表强制转换为 PageHelper 插件的 Page 类型
需要注意的是,向 dao 层传参的时候,就传它需要的查询字段就可以了(我之前把 page 和pageSize 也传了,完全是没必要的)
3.封装 PageBean 对象
我们先回顾一下我们定义的 PageBean 对象:
public class PageBean {
private Long total; // 总记录数
private List rows; // 当前页 数据列表
}
PageBean pageBean = new PageBean(p.getTotal(), p.getResult());
我们的最终目的是把这个 PageBean 打包带回给 controller 层,然后到 controller 层再进行包装为 Result 最终返回给前端,所以我们在这里直接调用 PageBean 的 getTotal() 和 getResult() 方法,拿到 数据量 和 经过 PageHelper 分页后的数据 List 表
- 第三步 dao 层
查询语句比较复杂,我们不用注解,使用了 XML 映射
XML 映射的要求是同包同名
然后在每个参数前加上注解
@param("参数名")
接下来在 XML 里面写 SQL 语句
这是之前写过的,比较简单
写完这个就完成了
4.3.3 测试
发起 GET 请求,查出一个人
其他的也都类似,不再进行重复演示
4.4 批量删除员工
比较简单,所以就直接实现了
第一步
第二步
接口省略了
第三步
4.5 新增员工
- 第一步
要特别注意的是,使用 POST 方法,在方法体中传递 JSON 数据,这个时候需要注解
@RequestBody
这样就把 JSON 格式的数据包装到后面指定的对象当中
- 第二步
- 第三步
在 Mapper 层用 XML 映射
测试