Tlias系统(部门管理)

Tlias系统(部门管理)

1.前言

1.1Restful风格

1.2工程搭建

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

    • 创建项目

      image-20251205100101414

  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:4`` ``: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.Driver
          url: jdbc:mysql://localhost:3306/tlias
          username: root
          password: 1234
      mybatis:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
      
  3. 准备基础包结构, 并引入实体类Dept及统一的响应结果封装类Result

    • 准备基础包结构

      image-20251205100519425

    • 实体类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;
          }
      
      }
      
    • 基础代码结构

      image-20251205101006452

    • 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 {
      }
      

2. 查询部门

2.1 基本实现

image-20251205101639579

  • Controller层,负责接收前端发起的请求,并调用service查询部门数据,然后响应结果。
  • Service层,负责调用Mapper接口方法,查询所有部门数据。
  • Mapper层,执行查询所有部门数据的操作。
代码实现
  1. Conntroller层

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

    /**
     * 部门管理控制器
     */
    @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方法,代码如下:

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

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

    @Service
    public class DeptServiceImpl implements DeptService {
        
        @Autowired
        private 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();
    }
    
接口测试

如何限制请求方式:

方法一:

  • 在controller方法的@RequestMapping注解中通过method属性来限定。
@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

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

方式二:

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

    @Autowired
    private DeptService deptService;

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

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

  • GET方式:@GetMapping
  • POST方式:@PostMapping
  • PUT方式:@PutMapping
  • DELETE方式:@DeleteMapping
数据封装
  • 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。

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

    解决方案:

    • 手动结果映射

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

    @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();
    
    • 起别名
    • 开启驼峰命名

2.2前后端联调

  • 运行nginx
  • 访问http://localhost:90
  • location:用于定义匹配特定uri请求的规则。
  • ^~ /api/:表示精确匹配,即只匹配以/api/开头的路径。
  • rewrite:该指令用于重写匹配到的uri路径。
  • proxy_pass:该指令用于代理转发,它将匹配到的请求转发给位于后端的指令服务器。

3.删除部门

3.1 需求

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

3.2 思路分析

image-20251205112656874

3.3 简单参数接收

  1. 方法一:通过原始的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();
    }
    
  2. 方法二: 通过Spring提供的@RequestParam注解,将请求的参数绑定给方法形参

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

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

    [!NOTE]

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

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

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

3.4 代码实现

1). Conteoller层

在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层

在DeptSevice中,增加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}

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

4. 新增部门

4.1 思路分析

image-20251205152437887

4.2 json参数接收

在Controller中, 需要接收前端传递的请求参数..

  • JSON格式的参数,通常会使用一个实体对象进行接收
  • 规则: JSON数据的键名与方法形参对象的属性名相同, 并需要使用@RequesBody注解标识

前端传递的请求参数格式为json,内容如下:{"name": "研发部"}

这里, 我们可以通过一个对象来接收, 只需要保证对象中有name属性

image-20251205153409007

4.3 代码实现

1). Conntroller层

在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语句中获取参数的时候, #{…}里面写的是对象的属性名(注意是属性名,不是表的字段名)

5.修改部门

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

5.1查询回显

5.1.1 思考

image-20251205155437595

5.1.2 路径参数接收

/depts/1这种在url中传递的参数, 我们称之为路径参数

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

image-20251205155837405

5.1.3 代码实现

1). Conntroller层

在DeptController中增加deptById方法

/**
 * 根据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中增加个tById方法

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

在DeptServiceImpl中增加deptbyId方法

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

5.2 修改数据

5.2.1 思路分析

image-20251205160750797

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

5.2.2 代码实现

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();
}
/**
 * 修改部门 - 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);
5.2.3 @RequestMapping

发现在Dept controller 中定义的方法,所有的请求路径, 都是/Depts开头的,我们可以将这个公共的路径/depts抽取到类上,那在各个方法上, 就可以省略这个/depts路径

@RequestMapping(“/depts”)

image-20251208133540544

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

6. 日志技术

6.1 概述

  • 为什么要记录日志

    • 便于追踪应用程序中的数据信息、程序的执行过程。
    • 便于对应用程序的性能进行优化。
    • 便于应用程序出现问题之后,排查问题,解决问题。
    • 便于监控系统的运行状态。
    • ... ...
  • 之前我们编写程序的时候, 也可以通过System.out.println(…)来输入日志, 为什么我们还要学习单独的日志技术呢

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

    • 硬编码.所有记录日志的代码, 都是硬编码, 没有办法做到灵活控制. 要想不输出这个日志了. 之恩个删掉记录日志的代码
    • 只能输入日志到控制台
    • 不便于程序的扩展.维护

6.2 日志入门

image-20251208144734206

  • JUL: 这是JavaSE平台提供的官方日志框架, 也被称为JUL. 配置相对简单, 但不够灵活, 性能较差
  • Log4j: 一个流行的日志框架, 提供了灵活的配置选项, 支持多种输出目标
  • Logback: 基于Log4j升级而来, 提供了更多的功能和配置选项, 性能优于Log4j
  • Slf4j: (Slimple Logging Facade for Java) 简单日志门面, 提供了一套日志操作的标准接口以及抽象类, 允许程序使用不同的底层日志框架

6.3 Logback入门

1). 准备工作: 引入Logback的依赖(springboot中无需引入, 在springboot中已经传递了此依赖)
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.4.11</version>
</dependency>
2). 引入配置文件logback.xml
<?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);

    @Test
    public 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("结束计算...");
    }

}

6.4 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">
    <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>

6.5 Logback日志级别

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

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

可以在配置文件Logback.xml中灵活的 控制输出那个类型的日志

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

6.6 案例日志记录

/**
 * 部门管理控制器
 */
@Slf4j
@RequestMapping("/depts")
@RestController
public class DeptController {

    @Autowired
    private DeptService deptService;

    /**
     * 查询部门列表
     */
    //@RequestMapping(value = "/depts", method = RequestMethod.GET)
    @GetMapping
    public Result list(){
        //System.out.println("查询部门列表");
        log.info("查询部门列表");
        List<Dept> deptList = deptService.findAll();
        return Result.success(deptList);
    }

    /**
     * 根据id删除部门 - delete http://localhost:8080/depts?id=1
     */
    @DeleteMapping
    public 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":"研发部"}
     */
    @PostMapping
    public 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":"研发部"}
     */
    @PutMapping
    public 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);
posted @ 2025-12-08 15:35  1忧郁女子  阅读(11)  评论(0)    收藏  举报