【JavaWeb】Tlias后台管理系统

文章目录

  • 1.班级管理
    • [1.1 班级列表查询 (分页查询)](#1.1 班级列表查询 (分页查询))
    • [1.2 查询所有员工(显示班主任)](#1.2 查询所有员工(显示班主任))
    • [1.3 新增班级](#1.3 新增班级)
    • [1.4 根据ID查询班级(注意查询了才能修改)](#1.4 根据ID查询班级(注意查询了才能修改))
    • [1.5 修改班级信息](#1.5 修改班级信息)
    • [1.6 删除班级信息](#1.6 删除班级信息)
      • [自定义异常 +全局异常处理器](#自定义异常 +全局异常处理器)
        • [🧩 一、什么是"异常"(Exception)?](#🧩 一、什么是“异常”(Exception)?)
        • [🧠 二、Java 为什么要有异常机制?](#🧠 二、Java 为什么要有异常机制?)
        • [🧩 三、Java 中的异常类型(按继承关系)](#🧩 三、Java 中的异常类型(按继承关系))
        • [🧩 四、为什么要自定义异常?](#🧩 四、为什么要自定义异常?)
        • [🧩 五、自定义异常的好处](#🧩 五、自定义异常的好处)
        • [🧩 六、异常处理的整体流程(项目中就是这样):](#🧩 六、异常处理的整体流程(项目中就是这样):)
        • [✅ 七、一句话总结](#✅ 七、一句话总结)

1.班级管理

1.1 班级列表查询 (分页查询)

controller层

ClazzController.java

java 复制代码
 /**
     * 分页查询班级
     */
    @GetMapping
    //这里是请求参数,用问号衔接
    public Result getClazz(ClazzQueryParam clazzQueryParam) { //专门写一个封装类,把请求参数封装到里面
        log.info("分页查询:{}", clazzQueryParam);
        PageResult<Clazz> pageResult =  clazzService.page(clazzQueryParam);
        return Result.success(pageResult);
    }

总结:

1.请求参数:这里根据接口文档可以看出,是一个请求参数(用?衔接),由于很长,所以可以专门写一个封装类,把请求参数封装到里面。如果不长,如果请求参数名与形参变量名相同,直接定义方法形参即可接收。(省略@RequestParam),不然可以用Spring提供的 @RequestParam 注解,将请求参数绑定给方法形参,如下:

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

2.使用PageResult做返回结构,里面有总数据个数和对应页面的值

service层

ClazzServiceImpl.java

java 复制代码
@Override
    public PageResult<Clazz> page(ClazzQueryParam clazzQueryParam) {
        PageHelper.startPage(clazzQueryParam.getPage(), clazzQueryParam.getPageSize());

        List<Clazz> clazzList = clazzMapper.list(clazzQueryParam); //传进去的作用是后面要读取

        if(!CollectionUtils.isEmpty(clazzList)){
            LocalDate now = LocalDate.now();
            for (Clazz clazz : clazzList) {
                LocalDate BeginDate = clazz.getBeginDate();
                LocalDate EndDate = clazz.getEndDate();
                if(BeginDate.isAfter(now)){
                    clazz.setStatus("未开班");
                }else if(now.isAfter(EndDate)){
                    clazz.setStatus("已结课");
                }else{
                    clazz.setStatus("在读中");
                }
            }
        }
        Page<Clazz> p = (Page<Clazz>) clazzList;
        return new PageResult<Clazz>(p.getTotal(), p.getResult());
    }

总结:

1.处理一下satus,其实在sql中用case when处理也行。

2.这里要做的是封装为PageResult返回给controller层。pagehelper所做的事情就是:

(1)拦截你的查询,自动加上 LIMIT 和 OFFSET,实现分页。

(2)执行了另一个 COUNT 查询来获取总数。SELECT COUNT(*) FROM emp WHERE ...这样它就知道数据库总共有多少条满足条件的记录。

3.clazzList其实已经是一个Page对象了,里面已经有total这个值,只是声明为了list,如果直接声明为page会很奇怪,因为Mapper 方法本身并不真的返回 Page,是 PageHelper 在中间动的手脚,所以强转为page也是一种约定,这样最后用p.getTotal(), p.getResult()即可获得PageResult需要的两个值

4.需要返回多个内容的时候,可以创建list返回,mybatis会自动封装到list里面

mapper层

xml 复制代码
  <!--id:方法名 resultType:查询返回的单条记录所封装的类型,注意是单条!!-->
    <select id="list" resultType = "org.example.pojo.Clazz">
        select c.*, e.name as masterName from Clazz c left join emp e on c.master_id = e.id
        <where>
            <if test="name != null and name != ''">
                c.name like concat('%', #{name}, '%')
            </if>
            <if test="begin != null and end != null">
                and c.end_date between #{begin} and #{end}
            </if>
        </where>
    </select>

总结:

1.实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装,开启驼峰命名映射的话也可以!!所以这里如果想把数据库返回的对应值封装到Clazz这个类中,只要开启驼峰命名,类似end_date会转为类中的endDate,可以封装成功。

1.2 查询所有员工(显示班主任)

1.3 新增班级

1.4 根据ID查询班级(注意查询了才能修改)

1.5 修改班级信息

1.6 删除班级信息

注意:在页面原型中,要求如果该班级下关联的有学生,是不允许删除的,并提示错误信息:"对不起, 该班级下有学生, 不能直接删除"。 (提示:可以通过自定义异常 + 全局异常处理器实现)

controller层

java 复制代码
    /**
     * 删除班级
     */
    @DeleteMapping("/{id}")//表示这里有个动态参数id
    //路径参数 使用@PathVariable绑定给方法形参!!
    public Result deleteClazz(@PathVariable Integer id) {
        log.info("删除班级路径参数:{}", id);
        clazzService.deleteClazzById(id);
        return Result.success();
    }

注意点:

1.这里是路径参数,也就是直接在/之后传入,需要用@PathVariable绑定给方法形参。并且DeleteMapping后面的路径("/{id}")要用大括号包裹起来,表示这是动态参数id。

Service层

ClazzService.java

java 复制代码
void deleteClazzById(Integer id);

ClazzServiceImpl.java

java 复制代码
    /**
     *根据id删除班级
     */
    @Override
    public void deleteClazzById(Integer id) {
        int studentCount = studentMapper.countByClazzId(id);
        if(studentCount > 0){
            //先创建一个对象,再抛出,然后被全局异常处理器捕获
            throw new DeletionNotAllowedException("对不起, 该班级下有学生, 不能直接删除");
        }
        clazzMapper.deleteClazzById(id);
    }

Mapper层

ClazzMapper.java

java 复制代码
void deleteClazzById(Integer id);

ClazzMapper.xml

xml 复制代码
<!--    删除班级-->
    <delete id="deleteClazzById">
        delete from clazz where id = #{id}
    </delete>

StudentMapper.java

java 复制代码
int countByClazzId(Integer id);

StudentMapper.xml

xml 复制代码
 <select id="countByClazzId" resultType ="Integer">
        select count(*) from student where clazz_id = #{id}
    </select>

注意事项:

一开始想的是select count(*) from student group by clazz_id having clazz_id = #{id} ,但是有点复杂了,其实不需要分组,因为已经知道id是多少了,直接用where计算就行。

另外having是对分组后的内容进行过滤,where是分组前。

一些关于分组查询的知识点复习:

注意事项:

  • 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义【原因可以看上面,因为不知道要选谁】
  • 执行顺序:where > 聚合函数 > having
    where与having区别(面试题)
  • 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
  • 判断条件不同:where不能对聚合函数进行判断,而having可以。

自定义异常 +全局异常处理器

创建异常类

java 复制代码
public class DeletionNotAllowedException  extends RuntimeException {
    public DeletionNotAllowedException(String message) {
        //使得在抛出自定义异常时,可以带上自定义错误信息,并通过父类机制打印出来或传递给全局异常处理器。
        super(message);
    }
}

全局异常处理器

java 复制代码
    @ExceptionHandler(DeletionNotAllowedException.class)
    public Result handleDeletionNotAllowedException(DeletionNotAllowedException e){
        //把异常传进来处理
        return Result.error(e.getMessage());
    }

整个流程大概是:

程序捕获异常->创建异常对象->被全局处理器捕获->输出保存进去的msg

所以要做的就是,新建一个自定义异常,写到全局处理器里,在对应位置抛出该异常


🧩 一、什么是"异常"(Exception)?

异常(Exception)就是程序运行时发生的错误事件

打断了正常的程序流程

简单说:

当代码遇到无法继续执行的情况时,JVM 就会抛出一个"异常对象",告诉你哪里出了问题。


🔧 举个例子:

java 复制代码
int a = 10 / 0;   // ❌ 除以零

运行结果:

复制代码
Exception in thread "main" java.lang.ArithmeticException: / by zero

这就叫"异常"。

它告诉我们:在运行时,出现了不能继续执行的情况。


🧠 二、Java 为什么要有异常机制?

因为错误是无法避免的

文件可能不存在、网络可能断开、数据可能为 null、输入可能错误......

如果没有异常机制,你就得在每一行代码都写 if...else 检查,非常麻烦。

Java 的异常机制帮你做了两件事:

  1. 检测错误:当程序出错时,JVM 自动创建异常对象;
  2. 处理错误 :你可以用 try...catch 来捕获、处理错误,让程序不崩溃。

💡 例子:

java 复制代码
try {
    int result = 10 / 0;   // 这里会出错
    System.out.println("结果:" + result);
} catch (ArithmeticException e) {
    System.out.println("出错了:" + e.getMessage());
}

输出:

复制代码
出错了:/ by zero

✅ 程序不会崩溃,还能优雅地提示错误。


🧩 三、Java 中的异常类型(按继承关系)
复制代码
Throwable
 ├── Error              ← 程序无法恢复(JVM错误,比如内存溢出)
 └── Exception
      ├── Checked Exception(受检异常)→ 编译器必须处理(比如IOException)
      └── RuntimeException(运行时异常)→ 可选处理(比如NullPointerException)

📘 举几个常见的:

异常类型 示例 含义
NullPointerException 对 null 调用了方法 空指针
ArithmeticException 除以0 数学错误
ArrayIndexOutOfBoundsException 访问数组越界 下标越界
IOException 文件或网络出错 I/O错误
SQLException SQL语句执行出错 数据库错误

🧩 四、为什么要自定义异常?

内置异常只能描述"技术错误",

比如"空指针"、"除0"、"SQL错误"。

但在真实业务中,你需要表达的是"业务逻辑错误"。


⚙️ 举个实际例子:

你的系统有一个"删除班级"的功能:

要求:如果班级下还有学生,就不允许删除。

用 Java 内置异常你只能抛 RuntimeException("不能删除")

但这太笼统了,别人根本不知道是哪种删除逻辑出错。

于是我们写一个更语义化的异常:

java 复制代码
public class DeletionNotAllowedException extends RuntimeException {
    public DeletionNotAllowedException(String message) {
        super(message);
    }
}

然后使用它:

java 复制代码
if (studentCount > 0) {
    throw new DeletionNotAllowedException("对不起,该班级下有学生,不能直接删除");
}

这时别人一看日志:

复制代码
org.example.exception.DeletionNotAllowedException: 对不起,该班级下有学生,不能直接删除

就一目了然:

👉 这是业务逻辑错误(禁止删除),不是程序崩溃。


🧩 五、自定义异常的好处
优点 说明
✅ 可读性高 一看类名就知道错误类型(如 DeletionNotAllowedException
✅ 可区分业务异常和系统异常 系统异常是代码错误,业务异常是逻辑错误
✅ 易于统一管理 可以在全局异常处理器中统一捕获并格式化返回给前端
✅ 更易调试 日志中清晰可见是哪种错误、在哪个模块触发的

🧩 六、异常处理的整体流程(项目中就是这样):
复制代码
1️⃣ 程序运行中发现错误
     ↓
2️⃣ 抛出异常(throw)
     ↓
3️⃣ Spring 全局异常处理器捕获 (@ExceptionHandler)
     ↓
4️⃣ 封装成 Result 对象返回前端
     ↓
5️⃣ 前端显示错误提示

比如:

json 复制代码
{
  "code": 0,
  "message": "对不起,该班级下有学生,不能直接删除"
}

✅ 七、一句话总结

异常 是程序在运行时报告错误的机制,
自定义异常 是为了让错误信息更符合业务语义,

从而让系统更可读、更易维护、更优雅。

一些知识点:

路径参数(pathvariable),请求参数(@RequestParam),json格式的参数(requestbody)

相关推荐
蒟蒻的工具人2 小时前
SSE实时推送订单状态
java·eventsource·sse协议
小蒜学长2 小时前
springboot基于Java的校园导航微信小程序的设计与实现(代码+数据库+LW)
java·spring boot·后端·微信小程序
王元_SmallA3 小时前
IDEA + Spring Boot 的三种热加载方案
java·后端
小苏兮3 小时前
【把Linux“聊”明白】编译器gcc/g++与调试器gdb/cgdb:从编译原理到高效调试
java·linux·运维·学习·1024程序员节
Java天梯之路3 小时前
04 数据类型转换
java
Acrelhuang4 小时前
小小电能表,如何撬动家庭能源革命?
java·大数据·开发语言·人工智能·物联网
jyd01244 小时前
MongoDB 与 Java 实体类型 LocalTime 时区转换问题解决方案
java·数据库·mongodb
一抓掉一大把4 小时前
RuoYI框架.net版本实现Redis数据隔离
java·开发语言
.格子衫.4 小时前
Maven高级
java·maven