当前位置: 首页 > java >正文

从0开始学习Java+AI知识点总结-23.web实战案例(班级和学生增删改查、信息统计)

一、项目概述

基于部门管理完成与部门管理类似

1.1 业务场景

在教育培训、学校管理等场景中,需要对班级信息(如开课时间、班主任、教室)和学生信息(如学历、违纪记录、所属班级)进行全生命周期管理,同时需要数据统计功能辅助决策。

1.2 核心功能模块

系统包含两大核心模块,功能清单严格遵循接口文档规范:

  • 班级管理:条件分页查询、新增班级、根据 ID 查询班级、修改班级、删除班级、查询所有班级(用于学生关联)
  • 学生管理:条件分页查询、新增学生、根据 ID 查询学生、修改学生、删除学生、违纪处理(累计违纪次数和扣分)

二、数据库设计

合理的数据库设计是系统稳定运行的基础,需满足第三范式(减少冗余)并建立必要关联。

2.1 班级表(clazz)

存储班级基本信息,包含唯一标识、时间范围、关联关系等核心字段。

字段名

类型

约束

说明

id

int unsigned

主键、自增

班级唯一 ID

name

varchar(30)

非空、唯一

班级名称(如 “202班”)

room

varchar(20)

可选

教室位置(如 “212 教室”)

begin_date

date

非空

开课时间(用于计算班级状态)

end_date

date

非空

结课时间

master_id

int unsigned

可选、关联员工表

班主任 ID(外键关联员工表)

subject

tinyint unsigned

非空

学科(1:Java,2: 前端,3: 大数据,4:Python,5:Go,6: 嵌入式)

create_time

datetime

可选

记录创建时间(审计字段)

update_time

datetime

可选

记录最后修改时间(审计字段)

创建 SQL

create table clazz(

  id int unsigned primary key auto_increment comment 'ID,主键',

  name varchar(30) not null unique comment '班级名称',

  room varchar(20) comment '班级教室',

  begin_date date not null comment '开课时间',

  end_date date not null comment '结课时间',

  master_id int unsigned null comment '班主任ID,关联员工表ID',

  subject tinyint unsigned not null comment '学科,1:java,2:前端,3:大数据,4:Python,5:Go,6:嵌入式',

  create_time datetime comment '创建时间',

  update_time datetime comment '修改时间'

)comment '班级表';

-- 初始化测试数据

INSERT INTO clazz VALUES

(1,'202班','212','2024-04-30','2024-06-29',10,1,'2024-06-01 17:08:23','2024-06-01 17:39:58'),

(2,'203班','210','2024-07-10','2024-01-20',3,2,'2024-06-01 17:45:12','2024-06-01 17:45:12'),

(3,'204班','108','2024-06-15','2024-12-25',6,1,'2024-06-01 17:45:40','2024-06-01 17:45:40');

2.2 学生表(student)

存储学生信息,通过clazz_id与班级表关联,包含个人属性和行为记录。

字段名

类型

约束

说明

id

int unsigned

主键、自增

学生唯一 ID

name

varchar(10)

非空

学生姓名

no

char(10)

非空、唯一

学号(如 “2022000001”)

gender

tinyint unsigned

非空

性别(1: 男,2: 女)

phone

varchar(11)

非空、唯一

手机号(用于联系)

id_card

char(18)

非空、唯一

身份证号(唯一标识)

is_college

tinyint unsigned

非空

是否来自院校(1: 是,0: 否)

address

varchar(100)

可选

联系地址

degree

tinyint unsigned

可选

最高学历(1: 初中,2: 高中,3: 大专,4: 本科,5: 硕士,6: 博士)

graduation_date

date

可选

毕业时间

clazz_id

int unsigned

非空、外键

所属班级 ID(关联 clazz 表 id)

violation_count

tinyint unsigned

非空、默认 0

违纪次数(初始为 0)

violation_score

tinyint unsigned

非空、默认 0

违纪扣分(初始为 0)

create_time

datetime

可选

创建时间

update_time

datetime

可选

最后修改时间

创建 SQL

create table student(

  id int unsigned primary key auto_increment comment 'ID,主键',

  name varchar(10) not null comment '姓名',

  no char(10) not null unique comment '学号',

  gender tinyint unsigned not null comment '性别,1:男,2:女',

  phone varchar(11) not null unique comment '手机号',

  id_card char(18) not null unique comment '身份证号',

  is_college tinyint unsigned not null comment '是否来自于院校,1:是,0:否',

  address varchar(100) comment '联系地址',

  degree tinyint unsigned comment '最高学历,1:初中,2:高中,3:大专,4:本科,5:硕士,6:博士',

  graduation_date date comment '毕业时间',

  clazz_id int unsigned not null comment '班级ID,关联班级表ID',

  violation_count tinyint unsigned default '0' not null comment '违纪次数',

  violation_score tinyint unsigned default '0' not null comment '违纪扣分',

  create_time datetime comment '创建时间',

  update_time datetime comment '修改时间',

  constraint fk_student_clazz foreign key (clazz_id) references clazz(id)

) comment '学员表';

-- 初始化测试数据

INSERT INTO student VALUES

(1,'段誉','2022000001',1,'18800000001','110120000300200001',1,'北京市昌平区建材城西路1号',1,'2021-07-01',2,0,0,'2024-11-14 21:22:19','2024-11-15 16:20:59'),

(2,'萧峰','2022000002',1,'18800210003','110120000300200002',1,'北京市昌平区建材城西路2号',2,'2022-07-01',1,0,0,'2024-11-14 21:22:19','2024-11-14 21:22:19');

三、实体类设计

实体类是 Java 代码与数据库表的映射,需包含表字段及业务扩展字段,推荐使用 Lombok 简化代码。

3.1 班级实体(Clazz)

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

import java.time.LocalDate;

import java.time.LocalDateTime;

@Data  // 自动生成get/set/toString

@NoArgsConstructor  // 无参构造

@AllArgsConstructor  // 全参构造

public class Clazz {

    private Integer id;  // 班级ID

    private String name;  // 班级名称

    private String room;  // 教室

    private LocalDate beginDate;  // 开课时间(LocalDate处理日期)

    private LocalDate endDate;  // 结课时间

    private Integer masterId;  // 班主任ID

    private Integer subject;  // 学科(1-6)

    private LocalDateTime createTime;  // 创建时间

    private LocalDateTime updateTime;  // 修改时间

    // 扩展字段(非数据库字段,用于前端展示)

    private String masterName;  // 班主任姓名(关联查询)

    private String status;  // 班级状态(未开班/在读/已结课)

}

3.2 学生实体(Student)

import lombok.AllArgsConstructor;

import lombok.Data;

import lombok.NoArgsConstructor;

import java.time.LocalDate;

import java.time.LocalDateTime;

@Data

@NoArgsConstructor

@AllArgsConstructor

public class Student {

    private Integer id;  // 学生ID

    private String name;  // 姓名

    private String no;  // 学号

    private Integer gender;  // 性别(1男2女)

    private String phone;  // 手机号

    private String idCard;  // 身份证号

    private Integer isCollege;  // 是否来自院校(1是0否)

    private String address;  // 联系地址

    private Integer degree;  // 最高学历(1-6)

    private LocalDate graduationDate;  // 毕业时间

    private Integer clazzId;  // 所属班级ID

    private Short violationCount;  // 违纪次数

    private Short violationScore;  // 违纪扣分

    private LocalDateTime createTime;  // 创建时间

    private LocalDateTime updateTime;  // 修改时间

    // 扩展字段

    private String clazzName;  // 班级名称(关联查询)

}

四、核心接口实现

4.1 班级管理模块

4.1.1 班级条件分页查询

接口文档规范

  • 请求路径:/clazzs
  • 请求方式:GET
  • 参数:name(班级名称模糊查询)、begin(结课时间开始)、end(结课时间结束)、page(页码)、pageSize(每页条数)
  • 响应:分页数据(总条数 + 班级列表,含状态)

实现步骤

  1. Controller 层:接收参数并调用 Service

@RestController

@RequestMapping("/clazzs")

public class ClazzController {

    @Autowired

    private ClazzService clazzService;

    // 条件分页查询班级

    @GetMapping

    public Result<PageInfo<Clazz>> getClazzList(

            @RequestParam(required = false) String name,

            @RequestParam(required = false) String begin,

            @RequestParam(required = false) String end,

            @RequestParam(defaultValue = "1") Integer page,

            @RequestParam(defaultValue = "10") Integer pageSize) {

        PageInfo<Clazz> pageInfo = clazzService.queryClazzByPage(name, begin, end, page, pageSize);

        return Result.success(pageInfo);

    }

}

  1. Service 层:分页查询 + 状态计算

@Service

public class ClazzServiceImpl implements ClazzService {

    @Autowired

    private ClazzMapper clazzMapper;

    @Override

    public PageInfo<Clazz> queryClazzByPage(String name, String begin, String end, Integer page, Integer pageSize) {

        // 开启分页(基于PageHelper插件)

        PageHelper.startPage(page, pageSize);

        // 查询数据

        List<Clazz> clazzList = clazzMapper.selectByCondition(name, begin, end);

        // 计算班级状态(核心业务逻辑)

        LocalDate now = LocalDate.now();

        for (Clazz clazz : clazzList) {

            if (now.isBefore(clazz.getBeginDate())) {

                clazz.setStatus("未开班");

            } else if (now.isAfter(clazz.getEndDate())) {

                clazz.setStatus("已结课");

            } else {

                clazz.setStatus("在读");

            }

        }

        return new PageInfo<>(clazzList);

    }

}

  1. Mapper 层:动态 SQL 条件查询

<mapper namespace="com.example.mapper.ClazzMapper">

    <!-- 条件查询班级 -->

    <select id="selectByCondition" resultType="Clazz">

        select * from clazz

        <where>

            <if test="name != null and name != ''">and name like concat('%', #{name}, '%')</if>

            <if test="begin != null and begin != ''">and end_date >= #{begin}</if>

            <if test="end != null and end != ''">and end_date <= #{end}</if>

        </where>

        order by update_time desc

    </select>

</mapper>

关键技巧

  • 分页插件PageHelper需在 Spring Boot 中配置依赖,自动拦截分页参数
  • 状态计算在 Service 层处理,避免数据库压力过大
  • 时间字段建议用LocalDate/LocalDateTime(Java 8+),避免Date的线程安全问题
4.1.2 新增班级

接口文档规范

  • 请求路径:/clazzs
  • 请求方式:POST
  • 请求体:name(班级名称)、room(教室)、beginDate(开课时间)、endDate(结课时间)、masterId(班主任 ID)、subject(学科)
  • 响应:成功 / 失败提示

实现步骤

  1. Controller 层:接收班级对象

@PostMapping

public Result addClazz(@RequestBody Clazz clazz) {

    clazzService.addClazz(clazz);

    return Result.success();

}

  1. Service 层:校验唯一性 + 设置时间

@Override

public void addClazz(Clazz clazz) {

    // 校验班级名称唯一性

    Clazz existing = clazzMapper.selectByName(clazz.getName());

    if (existing != null) {

        throw new BusinessException("班级名称已存在");

    }

    // 设置创建/修改时间

    LocalDateTime now = LocalDateTime.now();

    clazz.setCreateTime(now);

    clazz.setUpdateTime(now);

    // 插入数据库

    clazzMapper.insert(clazz);

}

关键校验

  • 非空校验:namebeginDateendDatesubject为必填项
  • 唯一性校验:班级名称不可重复(数据库唯一索引 + 代码校验双重保障)
  • 时间逻辑:beginDate需早于endDate(可在 Service 层添加校验)
4.1.3 删除班级

接口文档规范

  • 请求路径:/clazzs/{id}
  • 请求方式:DELETE
  • 参数:路径参数id(班级 ID)
  • 约束:若班级下有学生,不允许删除,返回提示 “该班级下有学生,不能直接删除”

实现步骤

  1. Controller 层:接收班级 ID

@DeleteMapping("/{id}")

public Result deleteClazz(@PathVariable Integer id) {

    clazzService.deleteClazz(id);

    return Result.success();

}

  1. Service 层:关联校验 + 删除

@Autowired

private StudentMapper studentMapper;

@Override

public void deleteClazz(Integer id) {

    // 校验班级是否有关联学生

    int count = studentMapper.countByClazzId(id);

    if (count > 0) {

        throw new BusinessException("对不起,该班级下有学生,不能直接删除");

    }

    // 执行删除

    clazzMapper.deleteById(id);

}

优化建议

  • 实际项目中可采用 “逻辑删除”(添加is_deleted字段),保留历史数据
  • 删除操作建议添加事务注解@Transactional,确保数据一致性
4.1.4 其他班级接口
  • 根据 ID 查询班级:请求路径/clazzs/{id}(GET),直接查询单条数据返回
  • 修改班级:请求路径/clazzs(PUT),接收完整班级对象,更新非空字段 + 修改时间
  • 查询所有班级:请求路径/clazzs/list(GET),返回全量班级列表(用于学生新增时选择班级)

4.2 学生管理模块

4.2.1 学生条件分页查询

接口文档规范

  • 请求路径:/students
  • 请求方式:GET
  • 参数:name(学生姓名)、degree(学历)、clazzId(班级 ID)、pagepageSize
  • 响应:分页数据(总条数 + 学生列表,含班级名称)

核心代码

Service 层关联查询班级名称:

@Override

public PageInfo<Student> queryStudentByPage(String name, Integer degree, Integer clazzId, Integer page, Integer pageSize) {

    PageHelper.startPage(page, pageSize);

    // 联表查询:学生表+班级表(获取班级名称)

    List<Student> studentList = studentMapper.selectByCondition(name, degree, clazzId);

    return new PageInfo<>(studentList);

}

Mapper 层 SQL:

<select id="selectByCondition" resultType="Student">

    select s.*, c.name as clazz_name

    from student s

    left join clazz c on s.clazz_id = c.id

    <where>

        <if test="name != null and name != ''">and s.name like concat('%', #{name}, '%')</if>

        <if test="degree != null">and s.degree = #{degree}</if>

        <if test="clazzId != null">and s.clazz_id = #{clazzId}</if>

    </where>

    order by s.update_time desc

</select>

4.2.2 违纪处理接口

接口文档规范

  • 请求路径:/students/violation/{id}/{score}
  • 请求方式:PUT
  • 参数:路径参数id(学生 ID)、score(扣分数)
  • 逻辑:违纪次数 + 1,违纪扣分 + score

实现步骤

  1. Controller 层:接收参数

@PutMapping("/violation/{id}/{score}")

public Result handleViolation(@PathVariable Integer id, @PathVariable Short score) {

    studentService.handleViolation(id, score);

    return Result.success();

}

  1. Service 层:原子性更新

@Override

@Transactional  // 确保更新操作原子性

public void handleViolation(Integer id, Short score) {

    // 校验参数

    if (score <= 0) {

        throw new BusinessException("违纪扣分必须为正数");

    }

    // 查询学生

    Student student = studentMapper.selectById(id);

    if (student == null) {

        throw new BusinessException("学生不存在");

    }

    // 更新违纪信息

    student.setViolationCount((short) (student.getViolationCount() + 1));

    student.setViolationScore((short) (student.getViolationScore() + score));

    student.setUpdateTime(LocalDateTime.now());

    studentMapper.updateById(student);

}

关键注意

  • 扣分数需校验为正数,避免恶意数据
  • 使用@Transactional确保 “次数 + 1” 和 “分数累加” 同时成功或失败
4.2.3 其他学生接口
  • 新增学生:请求路径/students(POST),校验学号、手机号、身份证号唯一性
  • 根据 ID 查询学生:请求路径/students/{id}(GET),返回单条学生详情
  • 修改学生:请求路径/students(PUT),更新学生信息 + 修改时间
  • 删除学生:请求路径/students/{ids}(DELETE),支持批量删除(参数为 ID 数组)

五、数据统计功能

5.1 班级人数统计

接口文档规范

  • 请求路径:/report/studentCountData
  • 请求方式:GET
  • 响应:班级名称列表 + 对应人数列表

实现 SQL

select c.name as clazz_name, count(s.id) as student_count

from clazz c

left join student s on c.id = s.clazz_id

group by c.id, c.name

order by student_count desc;

Service 层代码

@Override

public Map<String, Object> countStudentByClazz() {

    List<ClazzCountVO> countList = studentMapper.countByClazz();

    // 提取班级名称和人数到两个列表

    List<String> clazzList = countList.stream().map(ClazzCountVO::getClazzName).collect(Collectors.toList());

    List<Integer> dataList = countList.stream().map(ClazzCountVO::getCount).collect(Collectors.toList());

    // 封装响应

    Map<String, Object> result = new HashMap<>();

    result.put("clazzList", clazzList);

    result.put("dataList", dataList);

    return result;

}

5.2 学生学历统计

接口文档规范

  • 请求路径:/report/studentDegreeData
  • 请求方式:GET
  • 响应:学历名称(初中 / 高中 / 大专等)+ 对应人数

实现 SQL

select

    case degree

        when 1 then '初中'

        when 2 then '高中'

        when 3 then '大专'

        when 4 then '本科'

        when 5 then '硕士'

        when 6 then '博士'

        else '其他'

    end as degree_name,

    count(id) as student_count

from student

group by degree

order by degree;

六、实战注意事项

6.1 接口开发规范

  • 严格遵循接口文档的请求路径、参数名、响应格式,避免前后端联调纠纷
  • 统一响应格式:{code: 1/0, msg: "提示信息", data: 数据}(1 成功,0 失败)
  • 分页参数统一用page(页码)和pageSize(每页条数),默认值分别为 1 和 10

6.2 数据校验与异常处理

  • 数据校验:使用@Valid+JSR303 注解(如@NotBlank@Pattern)校验入参
  • 全局异常处理:通过@RestControllerAdvice统一捕获异常,返回标准化错误响应

@RestControllerAdvice

public class GlobalExceptionHandler {

    @ExceptionHandler(BusinessException.class)

    public Result handleBusinessException(BusinessException e) {

        return Result.error(e.getMessage());

    }

}

6.3 性能优化建议

  • 分页查询添加索引:如clazz.end_datestudent.clazz_id等频繁查询字段
  • 关联查询避免SELECT *,只查询需要的字段
  • 高频访问接口(如班级列表)可添加 Redis 缓存,减少数据库压力

七、总结与扩展

本文基于真实业务需求,完整实现了班级与学生管理系统的核心功能,涵盖了:

  • 数据库设计(表结构 + 关联关系)
  • 实体类定义(含扩展字段)
  • 核心接口开发(CRUD + 分页 + 条件查询)
  • 业务逻辑处理(状态计算、关联校验、违纪累计)
  • 数据统计(班级人数、学历分布)

通过本项目实战,你可以掌握 基本的增删改查。如果觉得本文有帮助,欢迎收藏关注,后续将分享更多实战案例!

http://www.xdnf.cn/news/18618.html

相关文章:

  • 【Prometheus】Prometheus监控Docker实战
  • C++编程语言:标准库:第36章——字符串类(Bjarne Stroustrup)
  • 【C语言16天强化训练】从基础入门到进阶:Day 8
  • Krea Video:Krea AI推出的AI视频生成工具
  • 知识蒸馏 Knowledge Distillation 序列的联合概率 分解成 基于历史的条件概率的连乘序列
  • 大模型——深度评测智能体平台Coze Studio
  • 2025-08-23 李沐深度学习19——长短期记忆网络LSTM
  • Kafka Streams vs Apache Flink vs Apache Storm: 实时流处理方案对比与选型建议
  • SpringBootWeb入门
  • Ollama 本地部署 Qwen2.5-7b
  • 搜索--常见面试问题
  • Android 之wifi连接流程
  • 使用 LangChain 和 Neo4j 构建知识图谱
  • 一文学会vue的动态权限控制
  • 00后AI创业者崛起与AI商业应用新玩法:从Mercor到历史人物复刻的机遇
  • 【剖析高并发秒杀】从流量削峰到数据一致性的架构演进与实践
  • MySQL GPG 密钥更新问题解决文档
  • Kubernetes网络服务全解析
  • Linux netfilter工作原理详解
  • Mac简单测试硬盘读写速度
  • 暴雨环境漏检率下降78%!陌讯动态融合算法在道路积水识别的工程突破
  • LeetCode 面试经典 150_数组/字符串_找出字符串中第一个匹配项的下标(23_28_C++_简单)(KMP 算法)
  • PyTorch 面试题及详细答案120题(71-85)-- 高级特性与工具
  • Base64 编码优化 Web 图片加载:异步响应式架构(Java 后端 + 前端全流程实现)
  • vue实现小程序oss分片上传
  • 合合信息acge模型获C-MTEB第一,文本向量化迎来新突破
  • 微前端架构核心要点对比
  • C++ 使用最新 MySQL Connector/C++(X DevAPI)+ CMake 完整教程
  • 力扣 30 天 JavaScript 挑战 第38天 (第九题)学习了 语句表达式的区别 高级函数 promise async await 节流
  • 《P3623 [APIO2008] 免费道路》