前言
前面写完部门管理、员工管理之后,这一篇继续往下走,重点来实现 day-11 的班级管理功能
需求:
- 班级条件分页查询
- 班级新增、修改、删除、详情查询
一、班级管理模块实现
1. 班级条件分页查询
功能需求
班级列表页需要支持:
- 按班级名称模糊查询
- 按开课时间筛选
- 按结课时间筛选
- 分页展示结果
- 额外显示班主任名称和班级状态
1.0准备-pojo层
css
public class studentQueryParam {
//封装条件查询参数
private Integer page=1;
private Integer pageSize=10;
private String name;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate beginDate;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate endDate;
private Integer degree; // 学历
private Integer clazzId; // 班级ID
}
需要什么参数是看前端和数据库传递过来了什么内容
css
public class PageResult<T> {
private Long total;
private List<T> rows;
}
css
这里依旧是用于封装分页结果的,所以不用写,直接调用我们之前的即可
1.1 Controller 层实现
java
@GetMapping
public Result get(studentQueryParam studentQueryParam) {
log.info("条件查询参数:{}", studentQueryParam);
PageResult<Clazz> pageResult = clazzService.getbytime(studentQueryParam);
return Result.success(pageResult);
}
文字说明
这一层的职责很简单,就是接收前端传过来的分页和查询条件,然后调用业务层,最后把结果统一包装成 Result 返回给前端。
这里传入的不是一个个零散参数,而是 studentQueryParam 这个参数对象,这样代码会更整洁。
1.2 Service 层实现
java
@Override
public PageResult getbytime(studentQueryParam studentparam) {
PageHelper.startPage(studentparam.getPage(), studentparam.getPageSize());
List<Clazz> list = empMapper.getbyDate(studentparam);
Page<Clazz> p = (Page<Clazz>) list;
return new PageResult(p.getTotal(), p.getResult());
}
文字说明
业务层这里主要做了两件事:
- 通过
PageHelper.startPage(...)开启分页 - 调用 Mapper 查询数据,并把结果封装成分页对象返回
这里依旧像我们之前一样,调用pagehelper来实现分页的功能实现
1.3 Mapper 层实现
xml
<select id="getbyDate" resultType="com.heimait.pojo.Clazz">
SELECT
c.*,
e.name masterName,
CASE
WHEN c.end_date > CURDATE() THEN '在读'
WHEN c.end_date <= CURDATE() THEN '已结课'
ELSE '未开班'
END status
FROM clazz c
LEFT JOIN emp e ON c.master_id = e.id
<where>
<if test="name != null and name != ''">
AND c.name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="beginDate != null">
AND c.begin_date >= #{beginDate}
</if>
<if test="endDate != null">
AND c.end_date <= #{endDate}
</if>
</where>
ORDER BY c.update_time DESC
</select>
文字说明
这里的 SQL 有几个关键点:
LEFT JOIN emp:把班主任姓名一起查出来CASE WHEN:直接在 SQL 中计算班级状态<where>+<if>:根据前端是否传值,动态拼接查询条件ORDER BY c.update_time DESC:按修改时间倒序展示
因为这里用到模糊查询,所以我们用到where,if.
需求是显示三种开班状态,所以我们用case来判断
1.4 涉及知识点
1. PageHelper 分页插件
PageHelper.startPage(page, pageSize) 会在当前查询前生效,自动帮我们把普通查询变成分页查询。
2. 动态 SQL
MyBatis 中的 <if> 标签非常适合做条件查询。
谁有值就拼谁,没有值就不拼,避免手写大量字符串判断。
3. 左连接 LEFT JOIN
班级表中只存班主任 ID,不存班主任姓名。
所以查询时通过员工表补充 masterName,这就是关联查询的典型场景。
4. SQL 中计算业务字段
status 并不是数据库真实字段,而是查询时临时算出来的字段。
END:表示 CASE 表达式的结束
status:给这个 CASE 表达式起别名,结果集中的列名就是 status
这里补充一句:如果要完全严格实现"未开班、在读、已结课"三种状态,实际判断时最好同时结合 begin_date 和 end_date 两个字段。
2. 新增班级
功能需求
前端填写班级信息后,点击提交,将班级数据写入数据库。
2.1 Controller 层实现
java
@PostMapping
public Result add(@RequestBody Clazz clazz) {
log.info("添加班级参数:{}", clazz);
clazzService.add(clazz);
return Result.success(clazz);
}
2.2 Service 层实现
java
@Override
public void add(Clazz clazz) {
clazz.setCreateTime(LocalDateTime.now());
clazz.setUpdateTime(LocalDateTime.now());
empMapper.insert(clazz);
}
2.3 Mapper 层实现
xml
<insert id="insert">
INSERT INTO clazz (name, room,begin_date, end_date, master_id, subject,create_time, update_time)
VALUES (#{name}, #{room}, #{beginDate}, #{endDate}, #{masterId}, #{subject}, now(), now())
</insert>
文字说明
新增班级时,Controller 接收前端传来的 JSON 数据,Service 层补齐创建时间和修改时间,最后 Mapper 执行插入。
这也是三层架构中很常见的一种分工方式:
- Controller 只负责收参数
- Service 负责补业务字段
- Mapper 只管执行 SQL
涉及知识点
1. @RequestBody
用于接收前端传来的 JSON 请求体,并自动转换成 Clazz 对象。
2. 为什么时间字段放在 Service 层处理
因为时间字段属于业务规则的一部分。
如果写在 Controller 层,控制层就会参与业务;如果写在 Mapper 层,又会让 SQL 逻辑变重。
所以放在 Service 层最合适。
3. 根据 ID 查询班级详情
3.1 Controller 层实现
java
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
log.info("根据id查询班级信息:{}", id);
Clazz clazz = clazzService.getById(id);
return Result.success(clazz);
}
3.2 Service 层实现
java
@Override
public Clazz getById(Integer id) {
return empMapper.selectByKey(id);
}
3.3 Mapper 层实现
xml
<select id="selectByKey" resultType="com.heimait.pojo.Clazz">
SELECT *
FROM clazz
WHERE id = #{id}
</select>
文字说明
这个功能一般用于编辑回显。
前端点击"编辑"按钮后,会先根据班级 ID 查出当前班级的完整信息,然后回填到表单里。
涉及知识点
@PathVariable
@GetMapping("/{id}") 这种写法是 RESTful 风格中很常见的路径参数写法。
它表示从 URL 路径中提取参数,而不是从查询字符串中取值。
4. 修改班级
4.1 Controller 层实现
java
@PutMapping
public Result update(@RequestBody Clazz clazz) {
log.info("修改班级信息:{}", clazz);
clazzService.update(clazz);
return Result.success(clazz);
}
4.2 Service 层实现
java
@Override
public void update(Clazz clazz) {
clazz.setUpdateTime(LocalDateTime.now());
empMapper.update(clazz);
}
4.3 Mapper 层实现
xml
<update id="update">
UPDATE clazz
SET name = #{name},
room = #{room},
begin_date = #{beginDate},
end_date = #{endDate},
master_id = #{masterId},
subject = #{subject},
update_time = #{updateTime}
WHERE id = #{id}
</update>
文字说明
修改和新增的写法其实很像,不同点主要在于:
- 修改必须带
id - 新增要补创建时间和修改时间
- 修改只需要更新
updateTime
5. 删除班级
功能需求
删除班级时,如果该班级下已经有关联学生,则不能直接删除。
5.1 Controller 层实现
把id传入
java
@DeleteMapping("/{id}")
public Result delete(@PathVariable Integer id) {
log.info("删除班级信息:{}", id);
clazzService.delete(id);
return Result.success();
}
5.2 Service 层实现
用主动抛异常形式实现不可删除id需求
java
@Override
public void delete(Integer id) {
Integer count = empMapper.checkStudentCount(id);
if (count != null && count > 0) {
throw new RuntimeException("对不起 该班级下有学生 不能直接删除");
}
empMapper.delete(id);
}
5.3 Mapper 层实现
xml
<select id="checkStudentCount" resultType="java.lang.Integer">
SELECT COUNT(*) FROM student WHERE clazz_id = #{clazzId}
</select>
<delete id="delete">
delete from clazz where id = #{id}
</delete>
文字说明
这个功能不能只写一句 delete from clazz where id = #{id} 就结束。
因为班级和学生之间有关联关系,如果班级已经被学生引用,还直接删除,就会导致业务数据不一致。
所以这里先查数量:
- 如果班级下有学生,抛出异常
- 如果没有学生,再执行删除
涉及知识点
1. 业务删除校验
很多删除操作并不是"能删就删",而是要先判断关联数据是否存在。
这类校验通常应该写在 Service 层,而不是 Controller 层。
2. 自定义异常 / 全局异常处理
可以配合"自定义异常 + 全局异常处理器"实现。
这也是比较推荐的写法,因为这样可以把错误信息统一返回给前端,而不是让异常直接抛到页面。
6. 查询所有班级
6.1 Controller 层实现
java
@GetMapping("/list")
public Result getAll() {
log.info("查询所有班级信息");
List<Clazz> clazzList = clazzService.getAll();
return Result.success(clazzList);
}
6.2 Service 层实现
java
@Override
public List<Clazz> getAll() {
return empMapper.getAll();
}
6.3 Mapper 层实现
xml
<select id="getAll" resultType="com.heimait.pojo.Clazz">
SELECT * FROM clazz
</select>
总结
到这里,班级管理模块的核心接口基本就已经完成了,包括:
- 班级条件分页查询
- 新增班级
- 根据 ID 查询班级详情
- 修改班级信息
- 删除班级
- 查询所有班级
从三层架构的角度来看,这一部分已经把一个典型模块的开发流程完整走了一遍:
- Controller 层负责接收请求和响应结果
- Service 层负责处理业务逻辑和校验规则
- Mapper 层负责执行数据库操作
而"查询所有班级"这个接口,其实也为后面的学员管理做好了铺垫。因为学员新增、学员修改时,都需要先拿到班级列表,用来做下拉选择。
我是新手程序猿乐锅。本次分享到此结束,感谢大家的观看与支持!如果本期内容对您有帮助,欢迎点赞、收藏,您的支持将是我持续创作的最大动力,谢谢!