Lecture14-综合实战-员工管理模块

文章目录

    [toc]

    单词词性音标含义
    RESTfuladj./ˈrestfl/REST 风格(用 HTTP 方法语义表达操作:GET 查询、POST 新增、PUT 修改、DELETE 删除)
    Controllern./kənˈtrəʊlə(r)/控制器(表现层,接收 HTTP 请求并返回 JSON)
    Request Bodyn./rɪˈkwest ˈbɒdi/请求体(@RequestBody 把前端传来的 JSON 转成 Java 对象)
    Path Variablen./pɑːθ ˈveəriəbl/路径变量(@PathVariable 取出 URL 路径中的参数,如 /emp/1 中的 1)

    Lecture14-综合实战-员工管理模块

    一、实战目标

    把第 1~11 章的知识点串起来,做一个员工管理 RESTful 接口模块,包含:

    功能接口使用的知识点
    分页 + 多条件查询GET /emp/page分页插件 + LambdaQueryWrapper
    按主键查询GET /emp/{id}selectById
    新增员工POST /empinsert + 自动填充
    修改员工PUT /empupdateById(配合乐观锁)
    删除员工DELETE /emp/{id}逻辑删除
    按部门统计人数GET /emp/count/byDeptgroupBy + selectMaps

    数据库表使用最终形态(整合第 9、10 章的字段):

    CREATE TABLE t_emp (
        emp_id       INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工ID',
        emp_no       VARCHAR(20)   NOT NULL COMMENT '员工编号',
        emp_name     VARCHAR(50)   NOT NULL COMMENT '员工姓名',
        emp_salary   DECIMAL(10,2) COMMENT '员工工资',
        emp_birthday DATE          COMMENT '出生日期',
        phone_num    VARCHAR(20)   COMMENT '手机号',
        dept_id      INT           COMMENT '部门ID',
        deleted      TINYINT       NOT NULL DEFAULT 0 COMMENT '逻辑删除:0未删 1已删',
        create_time  DATETIME      COMMENT '创建时间',
        update_time  DATETIME      COMMENT '更新时间',
        version      INT           NOT NULL DEFAULT 0 COMMENT '乐观锁版本号'
    ) COMMENT '员工表';
    

    二、工程结构

    module-mp-demo
    ├── pom.xml
    └── src/main/java/com/atguigu/mp
        ├── ModuleMpApplication.java          # 主启动类
        ├── config/
        │   ├── MybatisPlusConfig.java        # 分页插件 + 乐观锁插件
        │   └── MyMetaObjectHandler.java      # 自动填充处理器
        ├── entity/
        │   └── Employee.java                 # 实体类
        ├── mapper/
        │   └── EmployeeMapper.java           # Mapper 接口
        ├── service/
        │   ├── EmployeeService.java          # Service 接口
        │   └── impl/
        │       └── EmployeeServiceImpl.java  # Service 实现
        └── controller/
            └── EmployeeController.java       # 控制器
    

    三、各层完整代码

    1、实体类 Employee

    package com.atguigu.mp.entity;
    
    import com.baomidou.mybatisplus.annotation.*;
    import lombok.Data;
    
    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    import java.util.Date;
    
    @Data
    @TableName("t_emp")
    public class Employee {
    
        @TableId(type = IdType.AUTO)
        private Integer empId;
    
        private String empNo;
    
        private String empName;
    
        private BigDecimal empSalary;
    
        private Date empBirthday;
    
        private String phoneNum;
    
        private Integer deptId;
    
        @TableLogic
        private Integer deleted;
    
        @TableField(fill = FieldFill.INSERT)
        private LocalDateTime createTime;
    
        @TableField(fill = FieldFill.INSERT_UPDATE)
        private LocalDateTime updateTime;
    
        @Version
        private Integer version;
    
    }
    

    2、Mapper 接口

    package com.atguigu.mp.mapper;
    
    import com.atguigu.mp.entity.Employee;
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface EmployeeMapper extends BaseMapper<Employee> {
    }
    

    3、Service 接口与实现

    package com.atguigu.mp.service;
    
    import com.atguigu.mp.entity.Employee;
    import com.baomidou.mybatisplus.extension.service.IService;
    
    public interface EmployeeService extends IService<Employee> {
    }
    
    package com.atguigu.mp.service.impl;
    
    import com.atguigu.mp.entity.Employee;
    import com.atguigu.mp.mapper.EmployeeMapper;
    import com.atguigu.mp.service.EmployeeService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import org.springframework.stereotype.Service;
    
    @Service
    public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService {
    }
    

    4、配置类

    package com.atguigu.mp.config;
    
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class MybatisPlusConfig {
    
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // 分页插件
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            // 乐观锁插件
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
            return interceptor;
        }
    
    }
    
    package com.atguigu.mp.config;
    
    import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
    import org.apache.ibatis.reflection.MetaObject;
    import org.springframework.stereotype.Component;
    
    import java.time.LocalDateTime;
    
    @Component
    public class MyMetaObjectHandler implements MetaObjectHandler {
    
        @Override
        public void insertFill(MetaObject metaObject) {
            this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
            this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
        }
    
    }
    

    5、控制器 EmployeeController

    package com.atguigu.mp.controller;
    
    import com.atguigu.mp.entity.Employee;
    import com.atguigu.mp.service.EmployeeService;
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.*;
    
    import java.math.BigDecimal;
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/emp")
    public class EmployeeController {
    
        @Autowired
        private EmployeeService employeeService;
    
        // ===== 1. 分页 + 多条件查询:GET /emp/page?pageNum=1&pageSize=3&name=张&deptId=1&minSalary=5000 =====
        @GetMapping("/page")
        public Page<Employee> page(@RequestParam(defaultValue = "1") Integer pageNum,
                                   @RequestParam(defaultValue = "3") Integer pageSize,
                                   @RequestParam(required = false) String name,
                                   @RequestParam(required = false) Integer deptId,
                                   @RequestParam(required = false) BigDecimal minSalary,
                                   @RequestParam(required = false) BigDecimal maxSalary) {
            Page<Employee> page = new Page<>(pageNum, pageSize);
    
            // 条件可选:值为 null 时自动忽略
            LambdaQueryWrapper<Employee> wrapper = new LambdaQueryWrapper<>();
            wrapper.like(name != null, Employee::getEmpName, name)
                   .eq(deptId != null, Employee::getDeptId, deptId)
                   .ge(minSalary != null, Employee::getEmpSalary, minSalary)
                   .le(maxSalary != null, Employee::getEmpSalary, maxSalary)
                   .orderByDesc(Employee::getEmpSalary);
    
            return employeeService.page(page, wrapper);
        }
    
        // ===== 2. 按主键查询:GET /emp/1 =====
        @GetMapping("/{id}")
        public Employee getById(@PathVariable Integer id) {
            return employeeService.getById(id);
        }
    
        // ===== 3. 新增员工:POST /emp(请求体 JSON)=====
        @PostMapping
        public boolean save(@RequestBody Employee employee) {
            // create_time / update_time 自动填充;主键自增回填
            return employeeService.save(employee);
        }
    
        // ===== 4. 修改员工:PUT /emp(请求体 JSON,携带 version)=====
        @PutMapping
        public boolean update(@RequestBody Employee employee) {
            // 乐观锁生效:前端必须回传 version,version 对不上则更新失败
            boolean updated = employeeService.updateById(employee);
            if (!updated) {
                throw new RuntimeException("数据已被他人修改,请刷新后重试");
            }
            return true;
        }
    
        // ===== 5. 删除员工:DELETE /emp/1(逻辑删除)=====
        @DeleteMapping("/{id}")
        public boolean delete(@PathVariable Integer id) {
            return employeeService.removeById(id);   // 实际执行 UPDATE deleted=1
        }
    
        // ===== 6. 按部门统计人数:GET /emp/count/byDept =====
        @GetMapping("/count/byDept")
        public List<Map<String, Object>> countByDept() {
            // 统计类查询:查询列不是实体属性,用 QueryWrapper + selectMaps
            QueryWrapper<Employee> wrapper = new QueryWrapper<>();
            wrapper.select("dept_id", "count(*) as empCount")
                   .groupBy("dept_id");
            return employeeService.listMaps(wrapper);
        }
    
    }
    

    四、测试

    工程中加入 spring-boot-starter-web 依赖后,启动主启动类,用浏览器 / Postman / IDEA HTTP Client 测试:

    测试项请求预期结果
    分页查询GET /emp/page?pageNum=1&pageSize=3返回 Page JSON:records 3 条、total 总数
    条件查询GET /emp/page?name=张只返回姓名含「张」的员工
    新增POST /emp + JSON返回 true,表中新增一条,create_time 自动填充
    修改PUT /emp + JSON(带 version)返回 true;再查 version 已 +1
    删除DELETE /emp/1返回 true;再查该员工查不到(deleted=1)
    部门统计GET /emp/count/byDept返回 [{dept_id:1, empCount:2}, ...]

    小提示:修改接口必须回传 version,否则乐观锁条件不满足更新失败——这正是乐观锁设计的体现。


    五、全教程知识地图

    知识点一句话总结章节
    环境搭建依赖 + 数据源 + 实体 + Mapper,跑通第一个查询第 2 章
    实体注解@TableName / @TableId / @TableField 让映射清晰可控第 3 章
    BaseMapper内置增删改查,一行 SQL 都不用写第 4 章
    QueryWrapper字符串列名拼条件第 5 章
    LambdaQueryWrapper方法引用拼条件,编译期安全,推荐第 6 章
    分页插件 + Page 对象,自动 count + limit第 7 章
    UpdateWrapper局部更新指定列第 8 章
    逻辑删除 / 自动填充删除不真删、时间自动维护第 9 章
    乐观锁version 校验,并发不丢更新第 10 章
    Service 层IService + 链式操作,业务代码更简洁第 11 章

    六、常见问题速查(全教程汇总)

    现象原因与解决
    启动报错mybatis-plus-spring-boot3-starter 用 3.5.9+;确认依赖已导入
    查不到数据 / 表不存在检查 @TableName;检查逻辑删除配置是否把 deleted=0 条件加上了
    分页不生效确认 MybatisPlusInterceptor 配置类被扫描
    更新失败返回 0 行检查乐观锁 version 是否有值;检查条件是否正确
    selectCount 类型不匹配3.5.x 返回 Long
    插入后主键为 null确认 @TableId(type = IdType.AUTO) 且表主键自增
    自定义 SQL 场景本教程未覆盖;实际项目中少量复杂 SQL 可在 Mapper 接口中用注解/XML 补充(后续课程讲解)

    七、结语

    到这里,MyBatis-Plus 的常用操作就学完了。回顾核心心法:

    单表操作不写 SQL:继承 BaseMapper / IService,方法开箱即用; 动态条件用 Wrapper:LambdaQueryWrapper + 可选条件,一个方法吃遍各种查询; 通用能力靠插件:分页、乐观锁、逻辑删除、自动填充,配一次全表生效。

    把这些能力组合起来,一个模块的增删改查接口几十分钟就能写完。后续学 Spring Boot、Spring Cloud 时,这套数据访问方式会一直伴随你。

    end