Web后端开发_04
SpringBootWeb案例_01
原型展示
成品展示
准备工作
需求&环境搭建
需求说明:
完成tlias智能学习辅助系统的部门管理,员工管理
环境搭建
- 准备数据库表(dept、emp)
- 创建springboot工程,引入对应的起步依赖(web、mybatis、mysql驱动、lombok)
- 配置文件
application.properties
中引入mybatis的配置信息,准备对应的实体类 - 准备对应的Mapper、Service(接口、实现类)、Controller基础结构
示例工程文件
数据准备
-- 部门管理
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());
创建工程
添加依赖
配置文件application.properties
中引入mybatis的配置信息
#驱动类名称
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#数据库连接的url
spring.datasource.url=jdbc:mysql://localhost:3306/tlias
#连接数据库的用户名
spring.datasource.username=root
#连接数据库的密码
spring.datasource.password=123456
#配置mybatis的日志, 指定输出到控制台
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#开启mybatis的驼峰命名自动映射开关 a_column ------> aCloumn
mybatis.configuration.map-underscore-to-camel-case=true
准备对应的Mapper、Service(接口、实现类)、Controller基础结构
开发规范
案例基于当前最为主流的前后端分离模式进行开发
开发规范-Restful
- REST(REpresentational State Transfer),表述性状态转换,它是一种软件架构风格
注意事项
- REST是风格,是约定方式,约定不是规矩,可以打破
- 描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。如:users、emps、books等
-
前后端交互统一响应结果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); } }
开发流程
部门管理
需求说明
部门管理页面 , 管理员可以对部门信息进行管理,包括新增、修改、删除部门信息等。
页面开发规则
- 部门查询
1.1 查询全部数据(由于部门数据比较少,不考虑分页)。
- 新增部门
1.1 点击新增部门,会打开新增部门的页面。
1.2 部门名称,必填,唯一,长度为2-10位。
- 删除部门
弹出确认框 , 提示 “您确定要删除该部门的信息吗 ?” 如果选择确定 , 则删除该部门 , 删除成功后 , 重新刷新列表页面。 如果选择了 取消 , 则不执行任何操作。
查询部门
思路
DeptController.java
/**
* @ClassName DeptController
* @Description 部门管理的controller
* @Author Bowen
* @Date 2023/11/26 11:19
* @Version 1.0
**/
@Slf4j
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/depts")
public Result list() {
log.info("查询全部部门数据");
//调用service查询部门数据
List<Dept> deptList = deptService.list();
return Result.success(deptList);
}
}
注解
@Slf4j
日志管理的注解
log.info("查询全部部门数据");
调用该方法,控制台输出日志
@GetMapping("/depts")
GET请求方法的注解
DeptService.java
接口
/**
* 部门管理
*/
public interface DeptService {
/**
* 查询全部数据
* @return
*/
List<Dept> list();
}
DeptServiceImpl.java
部门接口实现类
/**
* @ClassName DeptServiceImpl
* @Description 部门 接口的实现类
* @Author Bowen
* @Date 2023/11/26 11:25
* @Version 1.0
**/
@Service
public class DeptServiceImpl implements DeptService {
@Autowired
private DeptMapper deptMapper;
@Override
public List<Dept> list() {
return deptMapper.list();
}
}
DeptMapper.java
接口
/**
* 部门管理
*/
@Mapper
public interface DeptMapper {
/**
* 查询全部部门
* @return
*/
@Select("select * from dept")
List<Dept> list();
}
API测试
前后端联调
- 将资料中提供的“前端工程”文件夹中的压缩包,拷贝到一个没有中文不带空格的目录下,解压。
- 启动nginx,访问测试:http://localhost:90
若使用自己下载的nginx,需要修改nginx安装目录的conf文件夹下的nginx.conf
文件的配置内容(在我的电脑上启动nginx.exe都会闪退,是否运行成功,需要在任务管理器中查看)
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 90;
server_name localhost;
location / {
root html;
index index.html index.htm;
}
location ^~ /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://localhost:8080;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
然后再将前端项目打包后的文件(dist文件夹下的所有文件)拷贝到nginx安装目录的html文件夹下
联调成功
删除部门
需求
思路
DeptController.java
/**
* 删除部门数据
* @return
*/
@DeleteMapping("/depts/{id}")
public Result delete(@PathVariable Integer id){
log.info("根据id删除部门:{}",id);
//调用service删除部门
deptService.delete(id);
return Result.success();
}
DeptService.java
接口
/**
* 删除部门
* @param id
*/
void delete(Integer id);
DeptServiceImpl.java
部门接口实现类
@Override
public void delete(Integer id) {
deptMapper.deleteById(id);
}
DeptMapper.java
接口
/**
* 根据ID删除部门
*
* @param id
*/
@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);
API测试
新增部门
需求
思路
实现
@RequestParam
的属性defaultValue可以来设置参数的默认值
DeptController.java
/**
* 新增部门
*/
@PostMapping("/depts")
public Result add(@RequestBody Dept dept) {
log.info("新增一个部门:{}", dept);
deptService.add(dept);
return Result.success();
}
DeptService.java
接口
/**
* 新增部门
* @param dept
*/
void add(Dept dept);
DeptServiceImpl.java
部门接口实现类
@Override
public void add(Dept dept) {
dept.setCreateTime(LocalDateTime.now());
dept.setUpdateTime(LocalDateTime.now());
deptMapper.insert(dept);
}
DeptMapper.java
接口
/**
* 插入一条部门信息
* @param dept
*/
@Insert("insert into dept(name, create_time, update_time) values (#{name}, #{createTime}, #{updateTime})")
void insert(Dept dept);
API测试
@RequestMapping
注意事项
- 一个完整的请求路径,应该是类上的
@RequestMapping
的value属性+方法上的@RequestMapping
的value属性。
修改部门
1、实现数据回显
根据ID查询
请求路径:/depts/{id}
请求方式:GET
接口描述:该接口用于根据ID查询部门数据
请求参数
参数格式:路径参数
参数说明:
响应数据
参数格式:application/json
参数说明:
响应数据样例:
{
"code": 1,
"msg": "success",
"data": {
"id": 1,
"name": "学工部",
"createTime": "2022-09-01T23:06:29",
"updateTime": "2022-09-01T23:06:29"
}
}
DeptController.java
/**
* 根据ID查询部门数据
*/
@GetMapping("/{id}")
public Result get(@PathVariable Integer id) {
log.info("根据id查部门:{}", id);
Dept dept = deptService.get(id);
return Result.success(dept);
}
DeptService.java
接口
/**
* 查询id
* @param id
* @return
*/
Dept get(Integer id);
DeptServiceImpl.java
部门接口实现类
@Override
public Dept get(Integer id) {
Dept dept = deptMapper.getById(id);
return dept;
}
DeptMapper.java
接口
/**
* 查找id
* @param id
* @return
*/
@Select("select * from dept where id = #{id}")
Dept getById(Integer id);
测试点击编辑出现部门名称
2、修改部门
基本信息
请求路径:/depts
请求方式:PUT
接口描述:该接口用于修改部门数据
请求参数
格式:application/json
参数说明:
请求参数样例:
{
"id": 1,
"name": "教研部"
}
响应数据
参数格式:application/json
参数说明:
响应数据样例:
{
"code":1,
"msg":"success",
"data":null
}
DeptController.java
/**
* 更新部门
*/
@PutMapping
public Result update(@RequestBody Dept dept) {
log.info("根据id修改部门:{}", dept.getId());
deptService.update(dept);
return Result.success();
}
DeptService.java
接口
/**
* 更新部门
* @param dept
*/
void update(Dept dept);
DeptServiceImpl.java
部门接口实现类
@Override
public void update(Dept dept) {
dept.setUpdateTime(LocalDateTime.now());
deptMapper.update(dept);
}
DeptMapper.java
接口
/**
* 更新部门信息
* @param dept
*/
@Update("update dept set name = #{name}, update_time = #{updateTime} where id = #{id}")
void update(Dept dept);
测试-修改成功
小结
部门管理
- 查询部门
@GetMapping
- 删除部门
@DeleteMapping
- 新增部门
@PostMapping
- 修改部门
@PostMapping
-根据ID查询、PutMapping
-修改部门
员工管理
分页查询
需求
思路
pageBean.java
分页查询结果封装类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageBean {
private Long total;//总记录数
private List<Emp> rows;//数据列表
}
EmpController.java
员工管理的controller
@Slf4j
@RestController
public class EmpController {
@Autowired
private EmpService empService;
@GetMapping("/emps")
public Result page(@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize) {
log.info("分页查询,参数:{},{}", page, pageSize);
//掉用service分页查询
PageBean pageBean = empService.page(page, pageSize);
return Result.success(pageBean);
}
}
EmpService.java
员工管理service层
public interface EmpService {
/**
* 分页查询
* @param page
* @param pageSize
* @return
*/
PageBean page(Integer page, Integer pageSize);
}
EmpServiceImpl.java
员工 接口实现类
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
public PageBean page(Integer page, Integer pageSize) {
//1. 获取总记录数
Long count = empMapper.count();
//2. 获取分页查询结果列表
Integer start = (page - 1) * pageSize;
List<Emp> empList = empMapper.page(start, pageSize);
//3. 封装PageBean对象
PageBean pageBean = new PageBean(count, empList);
return pageBean;
}
}
EmpMapper.java
员工管理mapper层,可能是mybatis版本不同,需要添加@Param("start")
和@Param("pageSize")
,可参考MyBatis框架_01中的参数名说明(http://t.csdnimg.cn/1ovab)
@Mapper
public interface EmpMapper {
//查询总记录数
@Select("select count(*) from emp")
public Long count();
//分页查询获取列表数据的方法
@Select("select * from emp limit #{start}, #{pageSize}")
public List<Emp> page(@Param("start") Integer start, @Param("pageSize") Integer pageSize);
}
API测试(无参)localhost:8080/emps
API测试(带参)localhost:8080/emps?page=3&pageSize=5
前端进行联调
小结
1、分页查询
- 请求参数:页码、每页展示记录数
- 响应结果:总记录数 、结果列表(PageBean)
2、注解
@RequestParam(defaultValue="1")//设置请求参数默认值
分页插件PageHelper
实现
pom.xml
引入依赖
<!--PageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.6</version>
</dependency>
EmpMapper.java
接口
@Select("select * from tlias.emp")
public List<Emp> list();
EmpServiceIml.java
员工 接口的实现类
@Override
public PageBean page(Integer page, Integer pageSize) {
//1. 设置分页参数
PageHelper.startPage(page, pageSize);
//2. 执行查询
List<Emp> empList = empMapper.list();
Page<Emp> p = (Page<Emp>) empList;
//3. 封装PageBean对象
PageBean pageBean = new PageBean(p.getTotal(), p.getResult());
return pageBean;
}
API测试
查看控制台,SELECT count(0) FROM tlias.emp
和select * from tlias.emp LIMIT ?
都是分页插件PageHelper生成的,
进行前后端联调
小结
PageHelper分页插件
引入依赖:
pagehelper-spring-boot-starter
使用:
PageHelper.startPage(pageNum, pageSize); List<Emp> List = empMapper.list(); Page<Emp> p = (Page<Emp>) List;
分页查询(带条件)
需求
思路
请求参数
参数格式:queryString
参数说明:
EmpController.java
员工管理的controller层
@Slf4j
@RestController
public class EmpController {
@Autowired
private EmpService empService;
@GetMapping("/emps")
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);
//掉用service分页查询
PageBean pageBean = empService.page(page, pageSize, name, gender, begin, end);
return Result.success(pageBean);
}
}
EmpService.java
员工管理的service层
public interface EmpService {
//分页查询
PageBean page(Integer page, Integer pageSize, String name, Short gender, LocalDate begin, LocalDate end);
}
EmpServiceImpl.java
员工 service接口的实现类
@Service
public class EmpServiceImpl implements EmpService {
@Autowired
private EmpMapper empMapper;
@Override
public PageBean page(Integer page, Integer pageSize, String name, Short gender, LocalDate begin, LocalDate end) {
//1. 设置分页参数
PageHelper.startPage(page, pageSize);
//2. 执行查询
List<Emp> empList = empMapper.list(name, gender, begin, end);
Page<Emp> p = (Page<Emp>) empList;
//3. 封装PageBean对象
PageBean pageBean = new PageBean(p.getTotal(), p.getResult());
return pageBean;
}
}
EmpMapper.java
员工Mapper层接口
@Mapper
public interface EmpMapper {
//员工信息查询
public List<Emp> list(@Param("name") String name, @Param("gender") Short gender,
@Param("begin") LocalDate begin, @Param("end") LocalDate end);
}
resource/com/bowen/mapper/EmpMapper.xml
使用动态SQL-XML映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bowen.mapper.EmpMapper">
<!-- 条件查询 -->
<select id="list" resultType="com.bowen.pojo.Emp">
select *
from tlias.emp
<where>
<if test="name != null and name != ''">
name like concat('%', #{name}, '%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
</mapper>
API测试,尽量测试的全面一些,以方便bug的修复
使用get请求
localhost:8080/emps?page=1&pageSize=5&name=张&gender=1&begin=2000-01-01&end=2010-01-01
localhost:8080/emps?page=1&pageSize=5&name=张&gender=1
localhost:8080/emps?page=1&pageSize=5&gender=1
localhost:8080/emps?name=张&gender=1
localhost:8080/emps?gender=1
在前端进行联调
删除员工
需求
接口文档
基本信息
请求路径:/emps/{ids}
请求方式:DELETE
接口描述:该接口用于批量删除员工的数据信息
请求参数
参数格式:路径参数
参数说明:
思路
EmpController.java
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids) {
log.info("批量删除操作,ids:{}", ids);
empService.delete(ids);
return Result.success();
}
EmpService.java
员工service层接口
/**
* 批量删除操作
* @param ids
*/
void delete(List<Integer> ids);
EmpServiceImpl.java
员工 接口的实现类
@Override
public void delete(List<Integer> ids) {
empMapper.delete(ids);
}
EmpMapper.java
员工Mapper层接口
/**
* 批量删除
* @param ids
*/
void delete(@Param("ids") List<Integer> ids);
resource/com/bowen/mapper/EmpMapper.xml
使用动态SQL-XML映射文件
<!--批量删除(1, 2, 3)-->
<delete id="delete">
delete
from tlias.emp
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
API测试
IDEA控制台中的日志,说明id=1,2,3的员工删除成功
前端联调,删除 张三丰、方东白
日志
a`
@DeleteMapping("/{ids}")
public Result delete(@PathVariable List<Integer> ids) {
log.info("批量删除操作,ids:{}", ids);
empService.delete(ids);
return Result.success();
}
EmpService.java
员工service层接口
/**
* 批量删除操作
* @param ids
*/
void delete(List<Integer> ids);
EmpServiceImpl.java
员工 接口的实现类
@Override
public void delete(List<Integer> ids) {
empMapper.delete(ids);
}
EmpMapper.java
员工Mapper层接口
/**
* 批量删除
* @param ids
*/
void delete(@Param("ids") List<Integer> ids);
resource/com/bowen/mapper/EmpMapper.xml
使用动态SQL-XML映射文件
<!--批量删除(1, 2, 3)-->
<delete id="delete">
delete
from tlias.emp
where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
API测试
IDEA控制台中的日志,说明id=15,16,17的员工删除成功
前端联调,删除 张三丰、方东白
日志