删除员工
- 删除员工基本信息
- 删除对应员工工作经历信息
修改员工
- 查询回显
- Mybatis中封装查询结果,什么时候用resultType,什么时候用resultMap
- 查询返回的字段名与实体的属性名可以直接对应上,用resultType
- 对应不上或者实体属性比较复杂,可以通过resultMap手动封装
- Mybatis中封装查询结果,什么时候用resultType,什么时候用resultMap
- 修改数据
异常处理
- try-catch:代码臃肿,不推荐
- 全局异常处理器
- @RestControllerAdvice = @ControllerAdvice + @ResponseBody
- @ExceptionHandler
员工信息统计
职位统计
case流程控制函数
- 语法一:
casewhen cond1 then res1[when cond2 then res2]elseres end;- 语法二(适用于等值匹配):
caseexpr when val1 then res1[when val2 then res2]elseres end;
性别统计
- if流程控制函数
- if(expr, val1, val2):如果表达式expr成立,取val1,否则取val2
- ifnull(expr, val1):如果expr不为null,取自身,否则取val1
- if流程控制函数
自主完成内容需求(含遇到的错误以及解决方案)
班级管理:
班级列表查询
获取参数并进行分页查询
📝 错误总结:
No static resource clazzs/1🔴 错误现象
访问
GET /clazzs/1时,后端报错:ERROR ... No static resource clazzs/1.🧠 根本原因
- 后端没有定义能处理路径
/clazzs/1的 Controller 方法。 - Spring MVC 在找不到匹配的接口后,尝试将其当作静态资源(如 HTML、JS 文件)去查找。
- 静态资源目录中也不存在
clazzs/1文件,最终返回 404 并记录此错误。
💡 关键点:
/clazzs/1是路径参数(Path Variable),不是查询参数(?id=1)。✅ 对比:为什么
@DeleteMapping能成功?@DeleteMappingpublicResultdelete(Integerid){...}- 实际请求是:
DELETE /depts?id=123(使用查询参数)。 - Spring 自动将
?id=123绑定到方法参数Integer id。 - Controller 路径为
/depts,与请求路径匹配 ✅。
而
GET /clazzs/1是路径传参,必须用@PathVariable接收,否则无法匹配!🛠️ 解决方案
在
ClazzController中添加详情接口:@RestController@RequestMapping("/clazzs")publicclassClazzController{@GetMapping("/{id}")publicResult<Clazz>getById(@PathVariableIntegerid){Clazzclazz=clazzService.getById(id);returnResult.success(clazz);}}✅ 此时
GET /clazzs/1就能正确映射到该方法。📌 最佳实践建议
操作 推荐写法 说明 查询列表 GET /clazzs可带查询参数(如 ?name=xx)查询详情 GET /clazzs/{id}使用 @PathVariable删除资源 DELETE /clazzs/{id}更符合 RESTful 规范(优于 ?id=1)⚠️ 避免混用:
- 路径参数 → 用
@PathVariable - 查询参数 → 用普通参数(自动绑定)
🔍 排查步骤
- 检查前端请求 URL:是
/clazzs/1还是/clazzs?id=1? - 检查后端是否有对应接口:
- 路径参数 → 需
@GetMapping("/{id}") + @PathVariable - 查询参数 → 只需方法参数,无需路径占位符
- 路径参数 → 需
- 确认 Controller 类上有
@RestController(确保返回 JSON 而非视图)
✅一句话总结:
“
No static resource xxx” =没有匹配的接口 + 也不是静态文件→ 补全 Controller 方法即可!- 后端没有定义能处理路径
使用QueryParam存储包括page和pagesize的值
使用PageHelper插件辅助分页查询
删除班级
添加班级
✅ 3.Controller 方法参数缺少
@RequestBody注解这是最常见的错误!如果你不加
@RequestBody,Spring 会尝试从请求参数(query/form-data)绑定数据,而不是从 JSON body。错误写法:
@PostMappingpublicResultsave(Clazzclazz){...}正确写法:
@PostMappingpublicResultsave(@RequestBodyClazzclazz){log.info("保存班级,数据:{}",clazz);returnResult.success();}🔥必须加上
@RequestBody,否则 Spring 不会解析 JSON body!修改班级
学员管理:学员列表查询、删除学员、添加学员、修改学员、违纪处理
删除学员
🆚 对比总结
特性 @RequestParam List<Integer> ids@PathVariable List<Integer> ids请求示例 /emps?ids=1,2,3/students/1,2,3是否支持逗号分割 ✅ 是(Spring 默认行为) ✅ 是(Spring 5+ 支持) 是否需要额外配置 ❌ 否 ❌ 否 RESTful 风格推荐 ⚠️ 一般用于过滤、分页等 ✅ 更适合资源标识(如批量删除) 空值处理 ?ids=→ 空列表或 null(可配)/students/→ 可能 404 或空字符串特殊字符风险 URL 编码即可 路径中不能有 /、?等❓ 那为什么你之前报错 “DELETE not supported”?
回到最初的问题:
你当时可能前端发的是
/students/1,2,3,但后端方法却是:@DeleteMappingpublicResultdelete(@RequestParamList<Integer>ids)// ← 期待 query param而
/students/1,2,3没有 query 参数,只有路径变量。Spring 找不到匹配的方法(因为路径
/students/1,2,3没有对应的@DeleteMapping("/{...}")),所以报:Request method 'DELETE' is not supported并不是 DELETE 被禁用,而是没有 handler 能处理这个 URL + 方法组合。
✅ 最佳实践建议
场景 推荐方式 批量删除资源(如 /students/1,2,3)✅ @PathVariable+ 路径风格带条件的查询/操作(如 ?status=active&ids=1,2,3)✅ @RequestParam大量 ID(>100) ⚠️ 考虑用 @RequestBody(避免 URL 过长)
数据统计:班级人数统计、学员学历统计
统计类数据接口开发规范与最佳实践
适用于后端返回图表数据(如柱状图、饼图)的场景,涵盖 SQL 设计、Java 处理、前后端协作。
一、前端图表常见数据格式
- 分离式结构(适用于柱状图、折线图)
{"code":1,"msg":"success","data":{"xAxis":["Java就业100期","Java就业101期",...],"series":[77,82,70,...]}}- 特点:X 轴标签与 Y 轴数值分开存储
- 适用图表:ECharts 柱状图、折线图等
- 对象数组式(适用于饼图、漏斗图)
{"code":1,"msg":"success","data":[{"name":"初中","value":5},{"name":"高中","value":6},...]}- 特点:每项包含名称和值
- 适用图表:ECharts 饼图、南丁格尔玫瑰图等
✅建议:团队统一约定一种格式,或根据图表类型灵活选择。
二、为什么使用
List<Map<String, Object>>作为 SQL 查询结果?原因 说明 动态列 统计结果(如“班级名+人数”)不属于任何实体类 灵活性高 无需为每个统计新建 DTO,快速开发 MyBatis 原生支持 <select resultType="java.util.Map">自动映射每行为Map字段可控 通过 SQL 的 AS明确指定 key 名(如AS pos,AS num)✅结论:
List<Map>是聚合查询的标准中间表示形式。三、SQL 编写规范:统一输出结构
无论统计维度如何,所有统计 SQL 应统一返回两个字段:
SELECT/* 分类名称(字符串) */COALESCE(clazz.name,'未分配班级')ASpos,/* 数量(整数) */COUNT(student.id)ASnumFROMstudentLEFTJOINclazzON...GROUPBYclazz.id;其他示例:
-- 岗位统计SELECTCASEjobWHEN1THEN'班主任'WHEN2THEN'讲师'ELSE'其他'ENDASpos,COUNT(*)ASnumFROMempGROUPBYjob;-- 学历统计SELECTCASEdegreeWHEN1THEN'初中'WHEN2THEN'高中'...ENDASpos,COUNT(*)ASnumFROMstudentGROUPBYdegree;✅关键:始终使用
AS pos和AS num,确保后端处理逻辑可复用。四、后端 Java 处理逻辑
- Mapper 接口(统一返回
List<Map>)
List<Map<String,Object>>countStudentCountData();2. Service 层:按前端需求转换
方案 A:分离式(返回两个 List)
publicclassChartOption{privateList<String>xAxis;// 或 clazzList / categoriesprivateList<Integer>series;// 或 dataList / values// 构造器 + getter/setter}// 转换逻辑List<Map<String,Object>>rawData=mapper.countXXX();List<String>xAxis=rawData.stream().map(m->(String)m.get("pos")).collect(Collectors.toList());List<Integer>series=rawData.stream().map(m->((Number)m.get("num")).intValue())// 注意:COUNT 可能返回 Long.collect(Collectors.toList());returnnewChartOption(xAxis,series);方案 B:对象数组式(推荐定义 DTO)
publicclassNameValue{privateStringname;privateIntegervalue;publicNameValue(Stringname,Integervalue){this.name=name;this.value=value;}// getter/setter}// 转换逻辑returnrawData.stream().map(m->newNameValue((String)m.get("pos"),((Number)m.get("num")).intValue())).collect(Collectors.toList());⚠️注意类型安全:
m.get("pos")→ 强转为Stringm.get("num")→ 先转Number,再调.intValue()(避免ClassCastException)
五、常见问题与解决方案
问题 原因 解决方案 X 轴显示为数字或 null 前端误用 item.num作 X 轴检查 xAxis.data = list.map(item => item.pos)返回数据缺少 pos字段学生未分配班级 → clazz.name = NULLSQL 中用 COALESCE(clazz.name, '未分配班级')图表空白 响应数据嵌套层级错误(如需 res.data.data)打印 console.log(res)确认结构No static resource错误后端未定义对应 Controller 方法 补全 @GetMapping("/xxx")接口六、最佳实践总结
- SQL 层
- 统一使用
AS pos(分类名)、AS num(数量) - 用
COALESCE处理NULL值 GROUP BY使用主键(如clazz.id)而非名称
- 统一使用
- Java 层
- Mapper 返回
List<Map<String, Object>> - Service 按前端需求转换为
ChartOption或List<NameValue> - 封装通用转换方法提高复用性
- Mapper 返回
- 前后端协作
- 明确约定数据格式(分离式 or 对象数组)
- 接口文档标注返回结构
- 前端通过
console.log验证数据
- 扩展性
- 新增统计只需:写 SQL + 调用通用转换逻辑
- 支持未来切换图表库(数据结构解耦)
七、附录:常用 SQL 函数
函数 作用 示例 COALESCE(a, b, c)返回第一个非 NULL 值 COALESCE(name, '未知')CASE WHEN ... END条件映射 CASE job WHEN 1 THEN '班主任' ... ENDCOUNT(*)/COUNT(col)计数 COUNT(student.id)(排除 NULL)✅
COALESCE是 ANSI SQL 标准,跨数据库兼容性优于IFNULL。注意:所有功能全部严格根据接口文档进行开发,并进行前后端联调