【学习总结|DAY029】后端Web实战(员工管理)一:多表操作与查询优化

在 Web 后端开发中,员工管理模块是企业级应用的重要组成部分。本文将深入探讨 Tlias 系统员工管理模块的开发过程,重点聚焦于多表关系处理、多表查询实现以及员工列表查询的优化,为开发者提供全面的技术参考。

一、多表关系剖析

(一)一对多关系(以部门与员工为例)

在实际业务场景中,一个部门通常会包含多个员工,这就形成了典型的一对多关系。在数据库设计层面,我们通过在员工表(emp)中添加外键 dept_id 来关联部门表(dept)的主键 id。例如,部门表 dept 结构如下:

create table dept(
  id int unsigned primary key auto_increment comment 'ID',
  name varchar(10) not null unique comment '部门名称',
  create_time datetime comment '创建时间',
  update_time datetime comment '修改时间'
) comment '部门表';

员工表 emp 结构如下:

create table emp(
  id int unsigned primary key auto_increment comment 'ID,主键',
  username varchar(20) not null unique comment '用户名',
  password varchar(32) not null comment '密码',
  name varchar(10) not null comment '姓名',
  gender tinyint unsigned not null comment '性别, 1:男, 2:女',
  phone char(11) not null unique comment '手机号',
  job tinyint unsigned comment '职位, 1:班主任,2:讲师...',
  salary int unsigned comment '薪资',
  image varchar(300) comment '头像',
  entry_date date comment '入职日期',
  create_time datetime comment '创建时间',
  update_time datetime comment '修改时间',
  dept_id int unsigned comment '关联的部门 ID'
) comment '员工表';

这种设计方式确保了数据的关联性和完整性,但在操作过程中,如果直接删除部门数据,可能会导致员工数据的孤立,引发数据不一致问题。为解决此问题,可引入外键约束,但需注意物理外键在增删改操作时会影响效率,且在分布式、集群场景下存在局限性,因此在实际应用中更推荐使用逻辑外键在业务层进行关联处理。

(二)一对一关系(以用户与身份证信息为例)

一对一关系常用于单表拆分,可提升操作效率。例如用户表(tb_user)和用户身份信息表(tb_user_card),在任意一方添加外键关联另一方主键,并将外键设置为唯一约束即可实现一对一关系。假设用户表包含基本信息:

id: name gender phone degree nationality birthday

用户身份信息表包含:

idcard issued expire_begin expire_end

通过在其中一张表中添加外键 user_id 关联另一张表的主键,并设置为 UNIQUE,就能建立两者的一对一联系。

(三)多对多关系(以学生与课程为例)

对于学生与课程的关系,一个学生可以选修多门课程,一门课程也可供多个学生选择,此时需要建立第三张中间表(tb_student_course)。中间表至少包含两个外键,分别关联学生表(tb_student)的主键和课程表(tb_course)的主键。例如学生表:

id no name

课程表:

id name

中间表:

id studentid courseid

这种结构能够准确地表达多对多的复杂关系,确保数据的多向关联。

二、多表查询攻略

(一)内连接查询

内连接用于获取两张表交集部分的数据。常见的语法有隐式内连接和显式内连接。隐式内连接示例:

select 字段列表 from 表 1, 表 2 where 连接条件...;

显式内连接示例:

select 字段列表 from 表 1 [inner] join 表 2 on 连接条件...;

在实际查询中,为简化书写,还可为表起别名,如:

select 字段列表 from 表 1 [as] 别名 1, 表 2 [as] 别名 2 where 条件...;

但需注意,一旦使用别名,后续引用字段时应使用别名替代表名。

(二)外连接查询

外连接分为左外连接和右外连接。左外连接可查询左表所有数据(包括交集部分),语法为:

select 字段列表 from 表 1 left [outer] join 表 2 on 连接条件...;

右外连接则查询右表所有数据(包括交集部分),语法为:

select 字段列表 from 表 1 right [outer] join 表 2 on 连接条件...;

在实际应用中,左外连接更为常用,因为右外连接的 SQL 通常可通过调换表顺序转换为左外连接,从而简化查询语句和逻辑。

(三)子查询

子查询即 SQL 语句中嵌套 select 语句,形式如:

select * from t1 where column1 = (select column1 from t2 …);

子查询外部语句可以是 insert /update/delete /select 等,其中以 select 最为常见。根据子查询在主查询中的位置和作用,可分为在 where 后作为条件和在 from 后作为子表两种类型。在编写包含子查询的 SQL 时,关键是要先对需求进行合理拆分,明确每一步的操作,再逐步构建 SQL 语句。

三、员工列表查询进阶

(一)基本查询与实体类封装

在查询所有员工信息并关联部门名称时,可使用如下 SQL 语句:

select e.*, d.name deptName from emp e left join dept d on e.dept_id = d.id;

对应的 Java 实体类 Emp 设计如下:

public class Emp {
    private Integer id; // ID,主键
    private String username; // 用户名
    private String password; // 密码
    private String name; // 姓名
    private Integer gender; // 性别,1:男,2:女
    private String phone; // 手机号
    private Integer job; // 职位,1:班主任, 2:讲师,3:学工主管, 4:教研主管,5:咨询师
    private Integer salary; // 薪资
    private String image; // 头像
    private LocalDate entryDate; // 入职日期
    private Integer deptId; // 关联的部门 ID
    private LocalDateTime createTime; // 创建时间
    private LocalDateTime updateTime; // 修改时间
    // 封装部门名称数据
    private String deptName; // 部门名称
}

通过这种方式,能够准确地获取和封装员工及所属部门的信息,满足业务展示需求。

(二)分页查询实现

分页查询是优化数据展示和提升系统性能的关键技术。在前后端交互中,前端需向后端传递当前页码(page)和每页展示记录数(pageSize)两个参数,后端则需返回总记录数(total)和当前页数据列表(rows)。

后端实现分页查询的示例代码如下:

@GetMapping
public Result page(@RequestParam(defaultValue = "1")Integer page,
                   @RequestParam(defaultValue = "10")Integer pageSize) {
    log.info("分页请求参数: {}, {}", page, pageSize);
    PageResult pageResult = empService.page(page, pageSize);
    return Result.success(pageResult);
}

在 Service 层:

public PageResult page(Integer page, Integer pageSize) {
    // 1. 获取总记录数
    Long count = empMapper.count();
    // 2. 获取每一页的数据列表
    Integer start = (page - 1) * pageSize;
    List empList = empMapper.list(start, pageSize);
    // 3. 封装分页结果
    return new PageResult(count, empList);
}

Mapper 层接口定义:

@Mapper
public interface EmpMapper {
    @Select("select count(*) from emp e left join dept d on e.dept_id = d.id ")
    public Long count(); // 查询总记录数
    @Select("select e.*, d.name deptName from emp e left join dept d on e.dept_id = d.id limit #{start},#{pageSize}")
    public List list(Integer start, Integer pageSize); // 查询结果列表
}

此外,还可使用 PageHelper 插件简化分页操作。使用步骤如下:

  1. 引入 PageHelper 依赖:

    com.github.pagehelper
    pagehelper-spring-boot-starter
    1.4.7

  1. 定义 Mapper 接口的查询方法(无需考虑分页):
// 查询员工数据
@Select("select e.* from emp e... ")
public List list();
  1. 在 Service 方法中实现分页查询:
public PageResult page(Integer page, Integer pageSize) {
    // 1. 设置分页参数
    PageHelper.startPage(page, pageSize);
    // 2. 调用 Mapper 接口方法
    List empList = empMapper.list();
    // 3. 解析并封装结果
    return new PageResult(...);
}

需注意,PageHelper 只会对紧跟其后的第一条 SQL 语句进行分页处理,且 SQL 语句结尾不要加分号。

(三)条件分页查询优化

在条件分页查询中,前端传递的参数除了分页信息外,还包括员工姓名(name)、性别(gender)、入职日期范围(begin 和 end)等查询条件。后端在接收参数时,对于日期时间类型参数,需使用 @DateTimeFormat 注解指定日期格式,如:

@GetMapping
public Result page(@RequestParam(defaultValue = "1")Integer page, @RequestParam(defaultValue = "10")Integer pageSize,
                   String name, Integer gender,
                   @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                   @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){
    log.info("分页查询,参数:{},{},{},{},{},{}",page,pageSize,name,gender,begin,end);
    PageResult pageResult = empService.page(page,pageSize,name,gender,begin,end);
    return Result.success(pageResult);
}

在 Mapper 层,可使用动态 SQL 来根据不同的查询条件生成灵活的 SQL 语句。例如:


其中,标签用于条件判断,当条件成立时拼接相应的 SQL 片段,标签则会根据查询条件智能生成 where 关键字,并自动去除条件前面多余的 and 或 or,确保 SQL 语句的正确性和简洁性。

综上所述,通过对 Tlias 系统员工管理模块的多表关系、多表查询及员工列表查询的深入学习和实践,我们掌握了一系列关键的数据库操作和后端开发技术,这些技术能够有效地应对企业级应用中复杂的数据管理和查询需求,为构建高效、稳定的员工管理系统奠定了坚实的基础。在实际项目开发中,开发者可根据具体业务场景灵活运用这些技术,不断优化系统性能和功能,提升用户体验。

你可能感兴趣的:(学习,java,maven,springboot,web,数据库)