Skip to content

部门管理


查询部门

思路分析

Controller 层,负责接收前端发起的请求,并调用 service 查询部门数据,然后响应结果

Service 层,负责调用 Mapper 接口方法,查询所有部门数据

Mapper 层,执行查询所有部门数据的操作

限制请求方式

问题分析

根据接口文档需求,以 GET 的方式请求,后端成功返回数据,但是经过测试,post、put、delete 请求方式均可成功发送请求并获取后端的响应数据,这时需要限制请求方式

@RequestMapping 的衍生注解

GET 方式:@GetMapping

POST 方式:@PostMapping

PUT 方式: @PutMapping

DELETE 方式:@DeleteMapping

方法一:在 controller 方法的 @RequestMapping 注解中通过 method 属性来限定

指定 value 属性为接口文档中指定的请求路径

java
package com.jacksonling.controller;

import com.jacksonling.pojo.Dept;
import com.jacksonling.pojo.Result;
import com.jacksonling.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 部门管理控制层
 */
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;
    @RequestMapping(value = "/depts", method = RequestMethod.GET)
    // 统一响应的结果封装类 Result
    public Result list(){
        List<Dept> deptList = deptService.findAll();
        return Result.success(deptList);
    }
}

⭐ 方法二:在 controller 方法上使用,@RequestMapping 的衍生注解,该注解制定了请求方式(推荐)

java
package com.jacksonling.controller;

import com.jacksonling.pojo.Dept;
import com.jacksonling.pojo.Result;
import com.jacksonling.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * 部门管理控制层
 */
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;
    @GetMapping("/depts")
    // 统一响应的结果封装类 Result
    public Result list(){
        List<Dept> deptList = deptService.findAll();
        return Result.success(deptList);
    }
}

数据封装

在上述测试中,我们发现部门的数据中,id、name 两个属性是有值的,但是 createTime、updateTime 两个字段值并未成功封装,而数据库中是有对应的字段值的,这是为什么呢?

(1) 实体类 属性名 和数据库表查询返回的 字段名 一致,mybatis 会自动封装

(2) 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装

提示:字段名指的是数据库中字段的名称,属性名指的是封装对象中的属性名

方法一:在 DeptMapper 接口方法上,使用 @Results @Result 注解进行手动结果映射

java
@Results({@Result(column = "create_time", property = "createTime"),
          @Result(column = "update_time", property = "updateTime")})
@Select("select id, name, create_time, update_time from dept")
public List<Dept> findAll();

方法二:起别名

java
@Select("select id, name, create_time createTime, update_time updateTime from dept")
public List<Dept> findAll();

⭐ 方法三:开启驼峰命名(推荐)

使用前提:实体类的属性与数据库表中的字段名严格遵守驼峰命名

在 application.yml 中添加如下配置,快捷键:camel

yml
mybatis:
  configuration:
    map-underscore-to-camel-case: true

安装 Nginx


打开浏览器,访问:http://localhost:90


Nginx 请求访问流程

前端工程请求服务器的地址为 http://localhost:90/api/depts,是如何访问到后端的 tomcat 服务器的?


其实这里,是通过前端服务 Nginx 中提供的反向代理功能实现的


(1)浏览器发起请求,请求的是 localhost:90 ,那其实请求的是 nginx 服务器

(2)在 nginx 服务器中呢,并没有对请求直接进行处理,而是将请求转发给了后端的 tomcat 服务器,最终由 tomcat 服务器来处理该请求

这个过程就是通过 nginx 的反向代理实现的。 那为什么浏览器不直接请求后端的 tomcat 服务器,而是直接请求 nginx 服务器呢,主要有以下几点原因


(1)安全:由于后端的 tomcat 服务器一般都会搭建集群,会有很多的服务器,把所有的 tomcat 暴露给前端,让前端直接请求 tomcat,对于后端服务器是比较危险的

(2)灵活:基于 nginx 的反向代理实现,更加灵活,后端想增加、减少服务器,对于前端来说是无感知的,只需要在 nginx 中配置即可

(3)负载均衡:基于 nginx 的反向代理,可以很方便的实现后端 tomcat 的负载均衡操作

具体的请求访问流程如下


(1)location:用于定义匹配特定 url 请求的规则

(2)^~ /api/:表示精确匹配,即只匹配以 / api / 开头的路径

(3)rewrite:该指令用于重写匹配到的 url 路径

(4)proxy_pass:该指令用于代理转发,它将匹配到的请求转发给位于后端的指令服务器

代码实现

(1)Controller 层

在 DeptController 中,增加 list 方法,代码如下

java
/**
 * 部门管理控制器
 */
@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    /**
     * 查询部门列表
     */
    @RequestMapping("/depts")
    public Result list(){
        List<Dept> deptList = deptService.findAll();
        return Result.success(deptList);
    }
}

(2) Service 层

在 DeptService 中,增加 findAll 方法,代码如下

java
public interface DeptService {
    /**
     * 查询所有部门
     */
    public List<Dept> findAll();
}

在 DeptServiceImpl 中,增加 findAll 方法,代码如下

java
@Service
public class DeptServiceImpl implements DeptService {

    @Autowired
    private DeptMapper deptMapper;

    public List<Dept> findAll() {
        return deptMapper.findAll();
    }
}

(3) Mapper 层

在 DeptMapper 中,增加 findAll 方法,代码如下

java
@Mapper
public interface DeptMapper {
    /**
     * 查询所有部门
     */
    @Select("select * from dept")
    public List<Dept> findAll();

}

删除部门

思路分析


接收请求参数的方法

方法一:通过原始的 HttpServletRequest 对象获取请求参数

java
/**
* 根据ID删除部门 - 简单参数接收: 方式一 (HttpServletRequest)
*/
@DeleteMapping("/depts")
public Result delete(HttpServletRequest request){
    String idStr = request.getParameter("id");
    int id = Integer.parseInt(idStr);

    System.out.println("根据ID删除部门: " + id);
    return Result.success();
}

方案二:通过 Spring 提供的 @RequestParam 注解,将请求参数绑定给方法形参

⚠️ 注意点

(1)@RequestParam 注解的 value 属性,需要 与前端传递的参数名保持一致

(2)@RequestParam 注解 required 属性默认为 true,代表该参数 必须传递,如果 不传递将报错。 如果参数可选,可以将属性设置为 false

(3)@RequestParam(value = "id", required = false) Integer deptId

(4)@RequestParam(defaultValue="默认值"),defaultValue 属性可用于设置请求参数默认值

java
@DeleteMapping("/depts")
public Result delete(@RequestParam("id") Integer deptId){
    System.out.println("根据ID删除部门: " + deptId);
    return Result.success();
}

方案三:如果请求参数名与形参变量名相同,直接定义方法形参即可接收(省略 @RequestParam

java
@DeleteMapping("/depts")
public Result delete(Integer id){
    System.out.println("根据ID删除部门: " + deptId);
    return Result.success();
}

代码实现

(1)Controller 层

在 DeptMapper 中,增加 delete 方法,代码实现如下

java
/**
 * 根据id删除部门 - delete http://localhost:8080/depts?id=1
 */
@DeleteMapping("/depts")
public Result delete(Integer id){
    System.out.println("根据id删除部门, id=" + id);
    deptService.deleteById(id);
    return Result.success();
}

(2)Service 层

在 DeptService 中,增加 deleteById 方法,代码实现如下

java
/**
 * 根据id删除部门
 */
void deleteById(Integer id);

在 DeptServiceImpl 中,增加 deleteById 方法,代码实现如下

java
public void deleteById(Integer id) {
    deptMapper.deleteById(id);
}

(3)Mapper 层

⚠️ 如果 mapper 接口方法形参只有一个普通类型的参数,#{…} 里面的属性名可以随便写,如:#{id}、#

⚠️ 如果有多个参数,需要使用 @Parm 注解指定参数名,具体见 MyBatis 笔记中 select 的相关笔记

在 DeptMapper 中,增加 deleteById 方法,代码实现如下

java
/**
 * 根据id删除部门
 */
@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);

新增部门

思路分析


接收 json 数据的方法

我们看到,在 controller 中,需要接收前端传递的请求参数,那接下来,我们就先来看看在服务器端的 Controller 程序中,如何获取 json 格式的参数

(1)JSON 格式的参数,通常会使用一个实体对象进行接收

(2)⚠️ 使用规则:JSON 数据的键名与方法形参对象的属性名相同,并需要使用 @RequestBody 注解标识

(3)理解记忆:@RequestBody 翻译为请求体,然而 JSON 数据就是在请求体中,然后发送给后端的

前端传递的请求参数格式为 json,内容如下:{"name":"研发部"},这里,我们可以通过一个对象来接收,只需要保证对象中有 name 属性即可


代码实现

(1) Controller 层

在 DeptController 中增加方法 save,具体代码如下

java
/**
 * 新增部门 - POST http://localhost:8080/depts   请求参数:{"name":"研发部"}
 */
@PostMapping("/depts")
public Result save(@RequestBody Dept dept){
    System.out.println("新增部门, dept=" + dept);
    deptService.save(dept);
    return Result.success();
}

(2) Service 层

在 DeptService 中增加接口方法 save,具体代码如下

java
/**
 * 新增部门
 */
void save(Dept dept);

在 DeptServiceImpl 中增加 save 方法,由于数据库中含有时间字段,这里需要在业务层手动设置然后传给 Mapper 层,完成添加部门的操作,具体代码如下

java
public void save(Dept dept) {
    //补全基础属性
    dept.setCreateTime(LocalDateTime.now());
    dept.setUpdateTime(LocalDateTime.now());
    //保存部门
    deptMapper.insert(dept);
}

(3)Mapper 层

java
/**
 * 保存部门
 */
@Insert("insert into dept(name,create_time,update_time) values(#{name},#{createTime},#{updateTime})")
void insert(Dept dept);

修改部门

思路分析


一、查询回显模块

接收路径参数

/depts/1,/depts/2 这种在 url 中传递的参数,我们称之为路径参数,那么如何接收这样的路径参数呢 ?

路径参数:通过请求 URL 直接传递参数,使用 {…} 来标识该路径参数,需要使用 @PathVariable 获取路径参数

⚠️ 如果路径参数名与 controller 方法形参名称一致@PathVariable 注解的 value 属性是可以省略


代码实现

(1)Controller 层

在 DeptController 中增加 getById 方法,具体代码如下

java
@GetMapping("/depts/{id}")
public Result getById(@PathVariable Integer id){
    System.out.println("根据ID查询, id=" + id);
    Dept dept = deptService.getById(id);
    return Result.success(dept);
}

(2) Service 层

在 DeptService 中增加 getById 方法,具体代码如下

java
/**
 * 根据id查询部门
 */
Dept getById(Integer id);

在 DeptServiceImpl 中增加 getById 方法,具体代码如下

java
public Dept getById(Integer id) {
    return deptMapper.getById(id);
}

(3)Mapper 层

在 DeptMapper 中增加 getById 方法,具体代码如下

java
/**
* 根据ID查询部门数据
*/
@Select("select id, name, create_time, update_time from dept where id = #{id}")
Dept getById(Integer id);

二、修改数据模块

思路分析

通过接口文档,我们可以看到前端传递的请求参数是 json 格式的请求参数,在 Controller 的方法中,我们可以通过 @RequestBody 注解来接收,并将其封装到一个对象中

代码实现

(1)Controller 层

在 DeptController 中增加 update 方法,具体代码如下

java
/**
 * 修改部门 - PUT http://localhost:8080/depts  请求参数:{"id":1,"name":"研发部"}
 */
@PutMapping("/depts")
public Result update(@RequestBody Dept dept){
    System.out.println("修改部门, dept=" + dept);
    deptService.update(dept);
    return Result.success();
}

(2)Service 层

在 DeptService 中增加 update 方法

java
/**
 * 修改部门
 */
void update(Dept dept);

在 DeptServiceImpl 中增加 update 方法。 由于是修改操作,每一次修改数据,都需要更新 updateTime。所以,具体代码如下

java
public void update(Dept dept) {
    //补全基础属性
    dept.setUpdateTime(LocalDateTime.now());
    //保存部门
    deptMapper.update(dept);
}

(3)Mapper 层

在 DeptMapper 中增加 update 方法,具体代码如下

java
/**
 * 更新部门
 */
@Update("update dept set name = #{name},update_time = #{updateTime} where id = #{id}")
void update(Dept dept);

优化请求路径写法

我们会发现,我们在 DeptController 中所定义的方法,所有的请求路径,都是 /depts 开头的,只要操作的是部门数据,请求路径都是 /depts 开头

那么这个时候,我们其实是可以把这个公共的路径 /depts 抽取到类上的,那在各个方法上,就可以省略了这个 /depts 路径。 代码如下

⚠️ 一个完整的请求路径,应该是类上的 @RequestMapping 的 value 属性 + 方法上的 @RequestMapping 的 value 属性