Restful风格

我们的案例是基于当前最为主流的前后端分离模式进行开发

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

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

基于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的用户

其中总结起来,就一句话:通过URL定位要操作的资源,通过HTTP动词(请求方式)来描述具体的操作。

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

  • GET : 查询

  • POST :新增

  • PUT : 修改

  • DELETE :删除

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

注意事项:

  • REST是风格,是约定方式,约定不是规定,可以打破

  • 描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。如:users、emps、books…

  1. 工程搭建

1). 创建SpringBoot工程,并引入web开发起步依赖、mybatis、mysql驱动、lombok

  • 创建项目

配置编码格式 

jdk版本

2). 创建数据库及对应的表结构,并在application.yml中配置数据库的基本信息。

  • 创建tlias数据库,并准备dept部门表。

    CREATE TABLE dept (id int unsigned PRIMARY KEY AUTO_INCREMENT COMMENT 'ID, 主键',name varchar(10) NOT NULL UNIQUE COMMENT '部门名称',create_time datetime DEFAULT NULL COMMENT '创建时间',update_time datetime DEFAULT NULL COMMENT '修改时间'
    ) COMMENT '部门表';INSERT INTO dept VALUES (1,'学工部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(2,'教研部','2023-09-25 09:47:40','2024-08-09 15:17:04'),(3,'咨询部','2023-09-25 09:47:40','2024-07-30 21:26:24'),(4,'就业部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(5,'人事部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(6,'行政部','2023-11-30 20:56:37','2024-07-30 20:56:37');

    application.yml 配置文件中配置数据库的连接信息

spring:application:name: tlias-web-management#mysql连接配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/tliasusername: rootpassword: 123456
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3). 准备基础包结构,并引入实体类Dept及统一的响应结果封装类Result

  • 准备基础包结构

  • 实体类Dept

package com.itheima.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {private Integer id;private String name;private LocalDateTime createTime;private LocalDateTime updateTime;
}
  • 统一响应结果Result

    package com.itheima.pojo;import lombok.Data;
    import java.io.Serializable;/*** 后端统一返回结果*/
    @Data
    public class Result {private Integer code; //编码:1成功,0为失败private String msg; //错误信息private Object data; //数据public static Result success() {Result result = new Result();result.code = 1;result.msg = "success";return result;}public static Result success(Object object) {Result result = new Result();result.data = object;result.code = 1;result.msg = "success";return result;}public static Result error(String msg) {Result result = new Result();result.msg = msg;result.code = 0;return result;}}
  • 基础代码结构

    • DeptMapper

      package com.itheima.mapper;import org.apache.ibatis.annotations.Mapper;@Mapper
      public interface DeptMapper {
      }
    • DeptService

      package com.itheima.service;public interface DeptService {
      }

    • DeptServiceImpl

    • package com.itheima.service.impl;import com.itheima.service.DeptService;
      import org.springframework.stereotype.Service;@Service
      public class DeptServiceImpl implements DeptService {
      }

    • DeptController

      package com.itheima.controller;import org.springframework.web.bind.annotation.RestController;/*** 部门管理控制器*/
      @RestController
      public class DeptController {
      }

    • 代码实现
    • 1). Controller层

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

      /*** 部门管理控制器*/
      @RestController
      public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*/@RequestMapping("/depts")public Result list(){List<Dept> deptList = deptService.findAll();return Result.success(deptList);}
      }

    2). Service层

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

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

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

    @Service
    public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptMapper deptMapper;public List<Dept> findAll() {return deptMapper.findAll();}
    }

    3). Mapper层

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

    @Mapper
    public interface DeptMapper {/*** 查询所有部门*/@Select("select * from dept")public List<Dept> findAll();}
    1. 接口测试

    启动项目,然后我们就可以打开Apifox进行测试了。

    :在controller方法上使用,@RequestMapping的衍生注解 @GetMapping。 该注解就是标识当前方法,必须以GET方式请求。

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

    上述两种方式,在项目开发中,推荐使用第二种方式,简洁、优雅。

    • GET方式:@GetMapping

    • POST方式:@PostMapping

    • PUT方式:@PutMapping

    • DELETE方式:@DeleteMapping

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

    原因如下:

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

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

    解决方案:

    • 手动结果映射

    • 起别名

    • 开启驼峰命名

      @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();

      1). 手动结果映射

      在DeptMapper接口方法上,通过 @Results及@Result 进行手动结果映射。

    说明:

    • @Results 注解源码:

    • @Result源代码:

    2). 起别名

    在SQL语句中,对不一样的列名起别名,别名和实体类属性名一样。

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

    3). 开启驼峰命名(推荐)

    如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射。驼峰命名规则: abc_xyz => abcXyz

    • 表中字段名:abc_xyz

    • 类中属性名:abcXyz

    在application.yml中做如下配置,开启开关。

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

    1. 前后端联调

    1. 联调测试

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

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

    3). 双击 nginx.exe 启动Nginx,一闪而过,就说明nginx已启动完成。

    如果在任务管理器中,能看到上述两个进程,就说明nginx已经启动成功。

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

    1. 请求访问流程

    前端工程请求服务器的地址为 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:用于定义匹配特定uri请求的规则。

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

    3. rewrite:该指令用于重写匹配到的uri路径。

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

    1. 删除部门

    1. 需求

    删除部门数据。在点击 "删除" 按钮,会根据ID删除部门数据。

    1. 思路分析

    明确了删除部门的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:

    1. 简单参数接收

    我们看到,在controller中,需要接收前端传递的请求参数。 那接下来,我们就先来看看在服务器端的Controller程序中,如何获取这类简单参数。 具体的方案有如下三种:

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

      /**
      * 根据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 注解,将请求参数绑定给方法形参

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

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

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

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

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

      对于以上的这三种方案呢,我们推荐第三种方案

    1. 代码实现

    1). Controller层

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

    /*** 根据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 方法,代码实现如下:

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

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

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

    3). Mapper层

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

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

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

    对于 DML 语句来说,执行完毕,也是有返回值的,返回值代表的是增删改操作,影响的记录数,所以可以将执行 DML 语句的方法返回值设置为 Integer。 但是一般开发时,是不需要这个返回值的,所以也可以设置为void。

    代码编写完毕之后,我们就可以启动服务,进行测试了

    1. 新增部门

    1. 需求

    点击 "新增部门" 的按钮之后,弹出新增部门表单,填写部门名称之后,点击确定之后,保存部门数据。

    1. 思路分析

    明确了新增部门的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:

    1. json参数接收

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

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

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

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

    1. 代码实现

    1). Controller层

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

    /*** 新增部门 - 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,具体代码如下:

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

    DeptServiceImpl中增加save方法,完成添加部门的操作,具体代码如下:

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

    3). Mapper层

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

     如果在mapper接口中,需要传递多个参数,可以把多个参数封装到一个对象中。 在SQL语句中获取参数的时候,#{...} 里面写的是对象的属性名【注意是属性名,不是表的字段名】。

    代码编写完毕之后,我们就可以启动服务,进行测试了。

    1. 修改部门

    对于任何业务的修改功能来说,一般都会分为两步进行:查询回显、修改数据。

    1. 查询回显

    1. 需求

    当我们点击 "编辑" 的时候,需要根据ID查询部门数据,然后用于页面回显展示。

    1. 思路分析

    明确了根据ID查询部门的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责

     

    1. 路径参数接收

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

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

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

    1. 代码实现

    1). Controller层

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

    /*** 根据ID查询 - GET http://localhost:8080/depts/1*/
    @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方法,具体代码如下:

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

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

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

    3). Mapper层

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

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

    代码编写完毕之后,我们就可以启动服务,进行测试了。

    1. 修改数据

    1. 需求

    查询回显回来之后,就可以对部门的信息进行修改了,修改完毕之后,点击确定,此时,就需要根据ID修改部门的数据。

    1. 接口描述

    参照参照课程资料中提供的接口文档。 部门管理 -> 修改部门

    1. 思路分析

    参照接口文档,梳理三层架构每一层的职责:

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

    1. 代码实现

    1). Controller层

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

    /*** 修改部门 - 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 方法。

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

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

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

    3). Mapper层

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

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

    代码编写完毕之后,我们就可以启动服务,进行测试了。修改完成之后,我们可以看到最新的数据,如下:

    1. @RequestMapping

    到此呢,关于基本的部门的增删改查功能,我们已经实现了。 我们会发现,我们在 DeptController 中所定义的方法,所有的请求路径,都是 /depts 开头的,只要操作的是部门数据,请求路径都是 /depts 开头。

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

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

    1. 日志技术

    1. 概述

    • 什么是日志?

      • 日志就好比生活中的日记,可以随时随地记录你生活中的点点滴滴。

      • 程序中的日志,是用来记录应用程序的运行信息、状态信息、错误信息的。

    • 为什么要在程序中记录日志呢?

      • 便于追踪应用程序中的数据信息、程序的执行过程。

      • 便于对应用程序的性能进行优化。

      • 便于应用程序出现问题之后,排查问题,解决问题。

      • 便于监控系统的运行状态。

      • ... ...

    • 之前我们编写程序时,也可以通过 System.out.println(...) 来输出日志,为什么我们还要学习单独的日志技术呢?

    这是因为,如果通过 System.out.println(...) 来记录日志,会存在以下几点问题:

    • 硬编码。所有的记录日志的代码,都是硬编码,没有办法做到灵活控制,要想不输出这个日志了,只能删除掉记录日志的代码。

    • 只能输出日志到控制台。

    • 不便于程序的扩展、维护。

    1. 日志框架

    2. JUL这是JavaSE平台提供的官方日志框架,也被称为JUL。配置相对简单,但不够灵活,性能较差。

    3. Log4j一个流行的日志框架,提供了灵活的配置选项,支持多种输出目标。

    4. Logback:基于Log4j升级而来,提供了更多的功能和配置选项,性能由于Log4j。

    5. Slf4j(Simple Logging Facade for Java)简单日志门面,提供了一套日志操作的标准接口及抽象类,允许应用程序使用不同的底层日志框架。

    1. Logback入门

    1). 准备工作:引入logback的依赖(springboot中无需引入,在springboot中已经传递了此依赖)

    <dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.4.11</version>
    </dependency>

     2). 引入配置文件 logback.xml (资料中已经提供,拷贝进来,放在 src/main/resources 目录下; 或者直接AI生成)

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度  %msg:日志消息,%n是换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder></appender><!-- 日志输出级别 --><root level="ALL"><appender-ref ref="STDOUT" /></root>
    </configuration>

    3). 记录日志:定义日志记录对象Logger,记录日志

    public class LogTest {//定义日志记录对象private static final Logger log = LoggerFactory.getLogger(LogTest.class);@Testpublic void testLog(){log.debug("开始计算...");int sum = 0;int[] nums = {1, 5, 3, 2, 1, 4, 5, 4, 6, 7, 4, 34, 2, 23};for (int i = 0; i < nums.length; i++) {sum += nums[i];}log.info("计算结果为: "+sum);log.debug("结束计算...");}}

    运行单元测试,可以在控制台中看到输出的日志,如下所示:

    1. Logback配置文件

    Logback日志框架的配置文件叫 logback.xml

    该配置文件是对Logback日志框架输出的日志进行控制的,可以来配置输出的格式、位置及日志开关等。

    常用的两种输出日志的位置:控制台、系统文件。

    1). 如果需要输出日志到控制台。添加如下配置:

    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder>
    </appender>

    2). 如果需要输出日志到文件。添加如下配置:

    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">日志输出文件的位置<file>./logs/talis.log</file><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件输出的文件名, %i表示序号 --><FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern><!-- 最多保留的历史日志文件数量 --><MaxHistory>30</MaxHistory><!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB --><maxFileSize>10MB</maxFileSize></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder>
    </appender>

    3). 日志开关配置 (开启日志(ALL),取消日志(OFF))

    <!-- 日志输出级别 -->
    <root level="ALL"><!--输出到控制台--><appender-ref ref="STDOUT" /><!--输出到文件--><appender-ref ref="FILE" />
    </root>
    1. <!-- 日志输出级别 -->
      <root level="info"><!--输出到控制台--><appender-ref ref="STDOUT" /><!--输出到文件--><appender-ref ref="FILE" />
      </root>

      Logback日志级别

    日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级由低到高):

    日志级别说明记录方式
    trace追踪,记录程序运行轨迹 【使用很少】log.trace("...")
    debug调试,记录程序调试过程中的信息,实际应用中一般将其视为最低级别 【使用较多】log.debug("...")
    info记录一般信息,描述程序运行的关键事件,如:网络连接、io操作 【使用较多】log.info("...")
    warn警告信息,记录潜在有害的情况 【使用较多】log.warn("...")
    error错误信息 【使用较多】log.error("...")

    可以在配置文件logback.xml中,灵活的控制输出那些类型的日志。(大于等于配置的日志级别的日志才会输出) 

    1. 案例日志记录

      /*** 部门管理控制器*/
      @Slf4j
      @RequestMapping("/depts")
      @RestController
      public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*///@RequestMapping(value = "/depts", method = RequestMethod.GET)@GetMappingpublic Result list(){//System.out.println("查询部门列表");log.info("查询部门列表");List<Dept> deptList = deptService.findAll();return Result.success(deptList);}/*** 根据id删除部门 - delete http://localhost:8080/depts?id=1*/@DeleteMappingpublic Result delete(Integer id){//System.out.println("根据id删除部门, id=" + id);log.info("根据id删除部门, id: {}" , id);deptService.deleteById(id);return Result.success();}/*** 新增部门 - POST http://localhost:8080/depts   请求参数:{"name":"研发部"}*/@PostMappingpublic Result save(@RequestBody Dept dept){//System.out.println("新增部门, dept=" + dept);log.info("新增部门, dept: {}" , dept);deptService.save(dept);return Result.success();}/*** 根据ID查询 - GET http://localhost:8080/depts/1*/@GetMapping("/{id}")public Result getById(@PathVariable Integer id){//System.out.println("根据ID查询, id=" + id);log.info("根据ID查询, id: {}" , id);Dept dept = deptService.getById(id);return Result.success(dept);}/*** 修改部门 - PUT http://localhost:8080/depts  请求参数:{"id":1,"name":"研发部"}*/@PutMappingpublic Result update(@RequestBody Dept dept){//System.out.println("修改部门, dept=" + dept);log.info("修改部门, dept: {}" , dept);deptService.update(dept);return Result.success();}
      }

      lombok中提供的@Slf4j注解,可以简化定义日志记录器这步操作。添加了该注解,就相当于在类中定义了日志记录器,就下面这句代码:

      private static Logger log = LoggerFactory. getLogger(Xxx. class);

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/web/88232.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/web/88232.shtml
    英文地址,请注明出处:http://en.pswp.cn/web/88232.shtml

    如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    Leetcode 3604. Minimum Time to Reach Destination in Directed Graph

    Leetcode 3604. Minimum Time to Reach Destination in Directed Graph 1. 解题思路2. 代码实现 题目链接&#xff1a;3604. Minimum Time to Reach Destination in Directed Graph 1. 解题思路 这一题思路上就是一个广度优先遍历&#xff0c;我们不断考察当前时间点以及位置…

    OpenXR Runtime切换工具-OpenXR-Runtime-Switcher

    在开发VR时&#xff0c;有时有多个设备&#xff0c;大家可能也会选择不同的串流工具&#xff0c;OpenXR类似于默认浏览器&#xff0c;如果设置错误可能导致游戏无法串流。 推荐一个工具&#xff0c;可以设置默认的OpenXR工具。 OpenXR-Runtime-Switcher 对于没有的设备&#…

    Opencv探索之旅:从像素变化到世界轮廓的奥秘

    在你已经能熟练地为图像施展“降噪”、“缩放”等魔法之后&#xff0c;你的探索之旅来到了一个全新的领域。你可能会好奇&#xff1a;我们人类能轻易地识别出照片中杯子的边缘、建筑的轮廓&#xff0c;那计算机是如何“看见”这些边界的呢&#xff1f;仅仅依靠滤波和颜色变换&a…

    Ubuntu 22.04 + MySQL 8 无密码登录问题与 root 密码重置指南

    背景场景 在 Ubuntu 系统中使用 apt 或 deb 包方式安装 MySQL 8 时&#xff1a; 初次安装后会自动初始化数据库&#xff1b;但 没有提示 root 初始密码&#xff1b;导致 mysql -u root -p 无法登录。 为了解决该问题&#xff0c;通常我们使用 --skip-grant-tables 方式跳过权限…

    题解:P13017 [GESP202506 七级] 线图

    首先明白定义&#xff1a; 线图 L(G)L(G)L(G) 的顶点对应原图 GGG 的边&#xff0c;当且仅当原图中的两条边有公共顶点时&#xff0c;对应的线图顶点之间有一条边。 不难想到&#xff0c;对于原图中的每个顶点 vvv&#xff0c;其度数 d(v)d(v)d(v) 对应的边集可以形成 (d(v)2)\…

    c++ duiLib环境集成2

    继续上一篇&#xff0c;现在需要把控制台隐藏&#xff0c;只显示调用duiLib框架显示的窗口。右键项目 → 属性 → 链接器 → 系统 → ‌子系统‌改为 窗口(/SUBSYSTEM:WINDOWS)。原来是这样&#xff1a;修改为&#xff1a;运行报错&#xff1a;需要修改入口函数为WinMain。如下…

    常见的网络攻击方式及防御措施

    常见的网络攻击方式及防御措施&#xff1a;全面解析网络安全威胁 前言肝文不易&#xff0c;点个免费的赞和关注&#xff0c;有错误的地方请指出&#xff0c;看个人主页有惊喜。 作者&#xff1a;神的孩子都在歌唱在信息化高速发展的今天&#xff0c;网络安全威胁无处不在&#…

    JavaScript 中导入模块时,确实不需要显式地写 node_modules 路径。

    1. 正确的导入语法在 Webpack、Vite 等打包工具中&#xff0c;node_modules 目录是默认的模块搜索路径&#xff0c;因此直接写包名即可&#xff1a;// ✅ 正确&#xff1a;直接使用包名import nprogress/nprogress.css;// ❌ 错误&#xff1a;不需要显式写 node_modules 路径im…

    ELK Stack技术栈

    文章目录一、日志收集所解决的问题二、Elastic Stack 组件介绍2.1 Elasticsearch2.2 Logstash2.3 Kibana2.4 Filebeat beats三、ELK Stack集群安装3.1 安装JAVA环境&#xff08;所有ES节点&#xff09;3.2 安装ES集群3.2.1 ES单节点部署3.2.2 ES JAVA调优&#xff1a;堆(heap)内…

    大腾智能国产 3D CAD:设计自由度拉满,数据安全锁死

    在智能制造与数字化转型的浪潮中&#xff0c;大腾智能CAD作为一款自主研发的三维计算机辅助设计软件&#xff0c;凭借其从概念设计到制造落地的全流程覆盖能力&#xff0c;正成为国产工业设计软件领域的新锐力量。软件深度融合先进建模技术与工程实践需求&#xff0c;为机械制造…

    ubuntu 操作记录

    1&#xff1a;安装minicom 1: sudo apt-get install minicom minicom -s 2&#xff1a;Ctrl Z C 的区别 ctrlz的是将任务中断,但是此任务并没有结束,他仍然在进程中他只是维持挂起的状态,用户可以使用fg/bg操作继续前台或后台的任务,fg命令重新启动前台被中断的任务,bg命令…

    深度剖析:向70岁老系统植入通信芯片——MCP注入构建未来级分布式通信

    > 如何让老旧系统重获新生?协议注入技术是关键。 ## 一、当遗留系统遇上分布式未来:一场艰难的对话 想象一下:你负责维护一套诞生于20年前的单体式银行核心系统,它像一位固执的70岁老人,使用着陈旧的TCP自定义协议。这时业务部门要求实现与云原生风险分析引擎的实时…

    针对 SSD 固态硬盘的安全擦除 Secure Erase

    SSD 的安全擦除&#xff08;Secure Erase&#xff09;用于永久删除存储介质上的数据&#xff0c;以及在驱动器性能开始明显下降至低于标称值时恢复其速度。Secure Erase 可以解决的问题核心当 SSD 开始运行缓慢&#xff08;读写数据变差&#xff09;时&#xff0c;这里有许多可…

    Three.js搭建小米SU7三维汽车实战(3)轨道控制器

    往期内容&#xff1a; Three.js搭建小米SU7三维汽车实战&#xff08;1&#xff09;搭建开发环境 Three.js搭建小米SU7三维汽车实战&#xff08;2&#xff09;场景搭建 轨道控制器 轨道控制器可以改变相机在空间坐标系中的位置 进而方便从不同的角度观察物体 1. 轨道控制器响…

    C++树状数组详解

    C树状数组深度解析 第1章 引言&#xff1a;为什么需要树状数组 1.1 动态序列处理的挑战 在现代计算机科学中&#xff0c;我们经常需要处理动态变化的序列数据&#xff0c;这类数据具有以下特点&#xff1a; 实时更新&#xff1a;数据点会随时间不断变化频繁查询&#xff1a;需要…

    TeamT5-ThreatSonar 解决方案:构建智能动态的 APT 与勒索软件防御体系

    一、核心功能深度解析&#xff1a;从威胁狩猎到自动化响应的闭环能力 &#xff08;一&#xff09;威胁狩猎&#xff1a;主动挖掘潜伏性攻击的 “数字侦探” 多层级威胁识别引擎&#xff1a; 静态特征匹配&#xff1a;内置超 1000 种 APT 后门签名&#xff08;如 Regin、Duqu 等…

    C#基础篇(10)集合类之列表

    C# 中的列表(List)详解列表(List)概述在C#中&#xff0c;List<T>是System.Collections.Generic命名空间中的一个泛型集合类&#xff0c;它提供了动态大小的数组功能&#xff0c;可以存储指定类型的元素。列表的创建与初始化// 创建一个空列表 List<int> numbers n…

    SpringBoot订单模块核心接口设计与实现

    目录 一、 管理端接口实现 (后台管理系统) 一、订单搜索 (高权重 - 核心管理功能) 1.Controller (OrderController): 2.Service (OrderService): 3.ServiceImpl (OrderServiceImpl): 1.使用MyBatis分页插件PageHelper 2.基础数据查询 4.Mapper (OrderMapper): 5.Mapper …

    EXCEL链接模板无法自动链接到PowerBI?试试这个方法

    在使用EXCEL链接模板连接PowerBI时&#xff0c;你有没有遇到如图所示的提示呢&#xff1a;下面我来分享一下&#xff0c;出现弹框的原因及解决方法&#xff1a;首先我们先看一下这个英文翻译&#xff0c;意思就是说&#xff0c;我们只能使一个PowerBI文件处于打开的状态&#x…

    最新全开源礼品代发系统源码/电商快递代发/一件代发系统

    简介&#xff1a;最新全开源礼品代发系统源码/电商快递代发/一件代发系统测试环境&#xff1a;Nginx PHP7.2 MySQL5.6图片&#xff1a;