十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot 3+MySQL 8学生选课系统实战

Spring Boot 3+MySQL 8学生选课系统实战 简介本资源是一套完整的Spring Boot毕业设计级学生选课系统实现方案面向计算机专业本科生、Java初学者及课程设计/毕设开发者解决高校教务场景中课程浏览、智能选课、成绩查询与教学反馈等核心业务需求。压缩包为ZIP格式大小21.91MB内含经实测可一键运行的全部源码、配套部署说明文档及功能演示视频涵盖Controller、Service、Mapper层完整结构及MySQL建表脚本与初始化数据便于快速本地部署与二次开发。目前已有280人学习下载资源由作者cuihwchn精心整理代码逻辑清晰、注释规范特别包含选课冲突检测、权限分级控制学生/管理员、课程评价模块及安全防护机制显著降低调试门槛。读者可直接复用项目架构快速掌握Spring Boot整合MyBatis、Thymeleaf、RESTful API设计等主流开发实践。1. 为什么一个“学生选课系统”能成为 Spring Boot MySQL 实战的黄金标本不是所有教学管理系统都值得从头写三遍——但学生选课系统是例外。它看似简单学生查课、选课、退课教师开课、录成绩管理员维护课程与用户权限。可一旦跑进真实校园场景它立刻暴露出高并发选课秒杀、跨学期课程复用、成绩录入原子性、选课冲突实时校验等典型业务压力点。正因如此它成了 Spring Boot 初学者验证 MVC 分层、事务控制、REST 接口设计的最小闭环也是中高级开发者检验 MyBatis 动态 SQL、连接池调优、MySQL 索引策略的天然沙盒。本项目标题里明确带出「源码部署说明视频演示」说明它不是玩具 Demo而是具备生产级结构的可运行工程Maven 多模块划分清晰、application.yml 配置分 profile 管理、SQL 脚本含建库建表初始数据、启动脚本封装了 jar 包运行与端口检查。你不需要从零搭环境但必须理解每一行配置背后的约束逻辑——比如为什么spring.jpa.hibernate.ddl-autovalidate在生产环境比update更安全为什么Transactional必须加在 Service 层而非 Controller。2. 用 Spring Boot 3.x MySQL 8.0 搭建选课系统骨架从依赖到主类的最小可行路径2.1 选型依据为什么是 Spring Boot 3.x 而非 2.xMySQL 8.0 的不可替代性在哪Spring Boot 3.x 是首个全面基于 Jakarta EE 9即jakarta.*命名空间的版本强制要求 JDK 17。这对选课系统意味着两点硬性收益一是java.timeAPI 原生支持更精准处理选课时间窗口如LocalDateTime直接映射 MySQL 的DATETIME(6)避免旧版Date类的时区陷阱二是 Spring Security 6.x 的默认 CSRF 策略更契合前后端分离架构防止学生重复提交选课请求。MySQL 8.0 则带来关键能力窗口函数用于按班级统计选课人数排名、CTE 递归查询处理课程先修关系树、以及utf8mb4_0900_as_cs排序规则对中文姓名、课程名的精确比对——这直接决定“张三”和“张叁”是否被判定为同一人。若用 MySQL 5.7你将被迫在 Java 层手写递归或放弃实时排名功能。提示不要跳过版本对齐检查。Spring Boot 3.2.x 对应 Spring Framework 6.1.x而 MyBatis 3.5.13 是目前唯一完全兼容 Jakarta EE 9 的稳定版。在pom.xml中必须显式声明properties java.version17/java.version spring-boot.version3.2.5/spring-boot.version mybatis.version3.5.13/mybatis.version /properties2.2 核心依赖配置排除自动配置陷阱精准引入必要模块仅靠spring-boot-starter-web和spring-boot-starter-jdbc无法支撑选课业务。必须显式添加以下依赖并处理冲突!-- Web 层基础 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- 数据访问核心使用 MyBatis 而非 JPA因选课系统需复杂动态 SQL -- dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version3.0.3/version !-- 注意MyBatis Starter 3.x 适配 Spring Boot 3.x -- /dependency !-- MySQL 8 驱动必须用 mysql-connector-java 8.0.33否则报时区错误 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- 连接池HikariCP 是 Spring Boot 3 默认但需手动调参 -- dependency groupIdcom.zaxxer/groupId artifactIdHikariCP/artifactId /dependency !-- Lombok 简化实体类选课系统中 Student、Course、Selection 关系繁杂 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency注意spring-boot-starter-data-jpa必须排除——本项目采用 MyBatis 手写 SQLJPA 的GeneratedValue会与 MySQL 的AUTO_INCREMENT冲突且 JPA 的二级缓存机制在选课高频读写场景下易导致脏数据。2.3 主启动类与关键配置禁用无用自动装配激活必要特性Application.java不只是启动入口更是系统行为的总开关SpringBootApplication( exclude { // 显式排除 DataSourceAutoConfiguration因我们手动配置 HikariCP DataSourceAutoConfiguration.class, // 排除 JPA 自动配置避免与 MyBatis 冲突 HibernateJpaAutoConfiguration.class, // 排除 RedisAutoConfiguration除非你真要加缓存 RedisAutoConfiguration.class } ) // 启用事务管理器这是选课操作选课扣学分更新余量原子性的基石 EnableTransactionManagement public class CourseSelectionApplication { public static void main(String[] args) { SpringApplication.run(CourseSelectionApplication.class, args); } }对应application.yml中必须定义 HikariCP 参数非默认值spring: datasource: url: jdbc:mysql://localhost:3306/course_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver hikari: # 连接池大小根据预估并发量设校园网峰值约 2000 人同时选课 maximum-pool-size: 50 # 连接超时选课页面加载需快速响应设为 3 秒 connection-timeout: 3000 # 空闲连接存活时间避免长连接被 MySQL kill idle-timeout: 600000 # 连接有效性检测用 MySQL 8 的 VALIDATE_CONNECTION connection-test-query: SELECT 1 # MyBatis 配置指定 mapper XML 位置与类型别名 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.example.course.entity configuration: # 开启驼峰命名自动转换student_id → studentId map-underscore-to-camel-case: true3. 学生选课核心业务落地从数据库建模到事务边界控制的完整链路3.1 MySQL 8 建库建表利用新特性解决选课系统特有难题选课系统最关键的三张表不是简单的student、course、selection而是必须包含业务约束的复合结构-- 创建数据库并指定排序规则解决中文模糊搜索不准问题 CREATE DATABASE course_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs; USE course_db; -- 学生表学号为主键姓名加全文索引支持模糊查重 CREATE TABLE student ( student_id VARCHAR(12) PRIMARY KEY COMMENT 学号如 20230001, name VARCHAR(20) NOT NULL COMMENT 姓名, major VARCHAR(30) COMMENT 专业, credit_balance INT DEFAULT 0 COMMENT 剩余学分, FULLTEXT(name) ) ENGINEInnoDB; -- 课程表增加先修课程字段JSON 格式存储多门先修课 ID CREATE TABLE course ( course_id VARCHAR(10) PRIMARY KEY COMMENT 课程编号如 CS101, title VARCHAR(100) NOT NULL COMMENT 课程名称, credit INT NOT NULL COMMENT 学分, max_capacity INT NOT NULL COMMENT 最大容量, current_enrolled INT DEFAULT 0 COMMENT 当前已选人数, prerequisite JSON COMMENT 先修课程列表如 [MATH201,ENG101], INDEX idx_title (title) -- 支持按课程名搜索 ) ENGINEInnoDB; -- 选课记录表联合主键 唯一索引防重复选课 CREATE TABLE selection ( student_id VARCHAR(12) NOT NULL, course_id VARCHAR(10) NOT NULL, selection_time DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) COMMENT 选课时间精度到毫秒, status ENUM(selected,dropped,waitlisted) DEFAULT selected, PRIMARY KEY (student_id, course_id), FOREIGN KEY (student_id) REFERENCES student(student_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES course(course_id) ON DELETE CASCADE, INDEX idx_status_time (status, selection_time) -- 支持按状态时间查询 ) ENGINEInnoDB;提示prerequisite JSON字段允许用 MySQL 8 的JSON_CONTAINS()函数校验先修条件例如SELECT * FROM course WHERE JSON_CONTAINS(prerequisite, MATH201) AND course_id CS301;这比在 Java 层解析 JSON 数组再查库快 3 倍以上。3.2 MyBatis XML 实现选课核心逻辑动态 SQL 与乐观锁保障并发安全选课操作本质是「减库存增记录更新余额」三步必须在一个事务内完成。MyBatis 的selectKey和if标签是关键!-- mapper/SelectionMapper.xml -- insert idselectCourse parameterTypemap !-- 1. 先检查课程是否满员乐观锁current_enrolled max_capacity -- update UPDATE course SET current_enrolled current_enrolled 1 WHERE course_id #{courseId} AND current_enrolled (SELECT max_capacity FROM course WHERE course_id #{courseId}) /update !-- 2. 若更新行数 0插入选课记录 -- insert INSERT INTO selection (student_id, course_id) VALUES (#{studentId}, #{courseId}) /insert !-- 3. 更新学生学分余额需校验学分是否足够 -- update UPDATE student SET credit_balance credit_balance - ( SELECT credit FROM course WHERE course_id #{courseId} ) WHERE student_id #{studentId} AND credit_balance ( SELECT credit FROM course WHERE course_id #{courseId} ) /update /insert对应的 Service 方法必须标注Transactional并捕获 SQL 异常Service public class SelectionService { Transactional(rollbackFor Exception.class) public Result selectCourse(String studentId, String courseId) { try { // 调用 MyBatis Mapper 执行上述 XML int updated selectionMapper.selectCourse(Map.of(studentId, studentId, courseId, courseId)); if (updated 0) { return Result.fail(选课失败课程已满或学分不足); } return Result.success(选课成功); } catch (DuplicateKeyException e) { return Result.fail(您已选过该课程); } catch (Exception e) { log.error(选课异常, e); throw new RuntimeException(系统繁忙请稍后重试, e); } } }注意Transactional的rollbackFor必须显式指定Exception.class因为 MyBatis 的SQLException是 checked exceptionSpring 默认只回滚 unchecked exception如RuntimeException。不加此参数选课失败时数据库状态将不一致。3.3 接口设计与参数校验RESTful 风格下的防御性编程Controller 层需做三层校验路径参数格式、请求体业务规则、数据库唯一性约束RestController RequestMapping(/api/selection) public class SelectionController { PostMapping(/{studentId}/courses/{courseId}) public Result selectCourse( PathVariable Pattern(regexp ^\\d{8,12}$) String studentId, PathVariable Length(min 3, max 10) String courseId, RequestBody Valid SelectionRequest request) { // 1. 格式校验由 Pattern/Length 完成 // 2. 业务校验检查学生是否存在、课程是否开放 if (!studentService.existsById(studentId)) { return Result.fail(学生不存在); } if (!courseService.isOpen(courseId)) { return Result.fail(课程未开放选课); } // 3. 调用 Service 执行选课 return selectionService.selectCourse(studentId, courseId); } } // 请求体校验 Data public class SelectionRequest { NotNull(message 学期不能为空) private String semester; // 如 2024-1 Min(value 1, message 优先级必须大于0) private Integer priority; // 用于候补队列排序 }4. 部署与运行从本地开发到 Linux 服务器的全流程实操指南4.1 MySQL 8.0 安装与初始化绕过常见坑点的保姆级步骤在 Ubuntu 22.04 上安装 MySQL 8.0必须避开官方 apt 源的旧版本陷阱# 1. 下载 MySQL APT 配置包确保是 8.0.x wget https://dev.mysql.com/get/mysql-apt-config_0.8.24-1_all.deb sudo dpkg -i mysql-apt-config_0.8.24-1_all.deb # 安装时选择 MySQL Server Cluster - 8.0 # 2. 更新并安装关键安装时会提示设置 root 密码务必记牢 sudo apt update sudo apt install mysql-server # 3. 修改默认认证插件否则 Spring Boot 连接报错 sudo mysql -u root -p mysql ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY your_strong_password; mysql FLUSH PRIVILEGES; # 4. 创建选课系统专用数据库与用户最小权限原则 mysql CREATE DATABASE course_db CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_as_cs; mysql CREATE USER course_applocalhost IDENTIFIED BY app_password_123; mysql GRANT SELECT,INSERT,UPDATE,DELETE ON course_db.* TO course_applocalhost; mysql FLUSH PRIVILEGES;提示utf8mb4_0900_as_cs是 MySQL 8.0 新增的排序规则对中文大小写敏感且支持 emoji。若用旧版utf8mb4_general_ci搜索“数据库原理”可能匹配不到“数据库原理双语”。4.2 Spring Boot 应用打包与 Linux 启动生产环境必备参数使用 Maven 打包后不能直接java -jar运行——必须指定配置文件与 JVM 参数# 1. 打包确保 profile 为 prod mvn clean package -Pprod # 2. 创建启动脚本 start.sh关键参数说明见下表 #!/bin/bash nohup java \ -Xms512m -Xmx1024m \ # 堆内存选课系统 1G 足够 -XX:UseG1GC \ # G1 垃圾回收器降低 STW 时间 -Dfile.encodingUTF-8 \ # 强制文件编码避免日志乱码 -Dspring.profiles.activeprod \ -jar target/course-selection-0.0.1-SNAPSHOT.jar \ --spring.config.locationfile:/opt/course/config/application-prod.yml \ /opt/course/logs/app.log 21 echo $! /opt/course/app.pidapplication-prod.yml必须覆盖开发配置spring: datasource: url: jdbc:mysql://127.0.0.1:3306/course_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: course_app password: app_password_123 # 关闭 HikariCP 的 auto-commit 检测生产环境由应用控制 datasource.hikari.auto-commit: false # 日志级别调为 WARN减少 I/O 压力 logging.level.root: WARN logging.level.com.example.course: INFO参数作用选课系统推荐值-Xms/-XmxJVM 堆内存初始/最大值-Xms512m -Xmx1024m避免频繁 GC-XX:UseG1GC启用 G1 垃圾回收器必选选课高峰期需低延迟--spring.config.location指定外部配置文件路径避免 jar 包内配置被误改nohup ... 后台运行并忽略挂起信号生产环境标准做法4.3 验证部署成功三步确认法排查常见故障部署后不等于可用必须执行以下验证检查进程与端口# 查看 Java 进程是否启动 ps -ef | grep course-selection # 检查 8080 端口是否监听Spring Boot 默认端口 netstat -tuln | grep :8080验证数据库连接# 进入 MySQL执行一条简单查询 mysql -u course_app -p -e SELECT COUNT(*) FROM course_db.student; # 输出应为具体数字而非 ERROR 1045调用健康检查接口Spring Boot Actuatorcurl -s http://localhost:8080/actuator/health | jq .status # 正常返回 UP # 检查数据库健康状态 curl -s http://localhost:8080/actuator/health | jq .components.db.status # 必须返回 UP否则检查 datasource 配置提示若curl返回404说明 Actuator 未启用。需在pom.xml中添加dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency并在application.yml中暴露端点management: endpoints: web: exposure: include: health,info,metrics5. 性能压测与索引优化用真实数据验证选课系统的并发承载力5.1 构造万级测试数据用 MySQL 8 的 CTE 递归快速生成人工插入 10000 条学生数据效率极低。利用 MySQL 8 的递归 CTE 一次性生成-- 生成 10000 名学生学号 20230001 ~ 20231000 WITH RECURSIVE nums AS ( SELECT 1 AS n UNION ALL SELECT n 1 FROM nums WHERE n 10000 ) INSERT INTO student (student_id, name, major, credit_balance) SELECT CONCAT(2023, LPAD(n, 4, 0)), CONCAT(学生, n), ELT(FLOOR(1 RAND() * 5), 计算机, 数学, 物理, 化学, 生物), FLOOR(10 RAND() * 20) FROM nums;同理生成 500 门课程及 50000 条选课记录。数据量上来后原始查询将明显变慢-- 未优化前查某学生所有已选课程含课程名、学分、开课院系 SELECT s.student_id, s.name, c.title, c.credit, c.current_enrolled FROM selection sel JOIN student s ON sel.student_id s.student_id JOIN course c ON sel.course_id c.course_id WHERE s.student_id 20230001; -- 执行时间 2s全表扫描5.2 精准添加复合索引针对高频查询模式设计分析慢查询日志slow_query_logON发现 90% 的慢 SQL 都落在selection表的关联查询上。必须建立覆盖索引-- 在 selection 表上创建复合索引覆盖 student_id course_id 查询 ALTER TABLE selection ADD INDEX idx_student_course (student_id, course_id); -- 在 course 表上创建覆盖索引避免回表查 title/credit ALTER TABLE course ADD INDEX idx_course_cover (course_id, title, credit, current_enrolled);再次执行相同查询时间降至 50ms 内。验证索引生效EXPLAIN FORMATTREE SELECT s.student_id, s.name, c.title, c.credit FROM selection sel JOIN student s ON sel.student_id s.student_id JOIN course c ON sel.course_id c.course_id WHERE s.student_id 20230001;输出中possible_keys应显示idx_student_coursekey应为该索引名。5.3 用 JMeter 模拟 1000 并发选课定位瓶颈与调优方向配置 JMeter 线程组线程数 1000Ramp-up 60 秒循环次数 1。HTTP 请求 URL 为POST http://localhost:8080/api/selection/${studentId}/courses/${courseId}其中${studentId}和${courseId}用 CSV Data Set Config 读取预生成的 1000 对 ID。关键监控指标吞吐量Throughput目标 ≥ 300 req/sec即每秒处理 300 次选课90% 响应时间≤ 800ms学生感知不卡顿错误率≤ 0.5%网络抖动可接受业务逻辑错误需为 0若吞吐量不足按顺序检查MySQL 连接池是否耗尽show status like Threads_connected;应 max_connectionsHikariCP 的active连接数是否持续 50说明数据库处理不过来top命令查看 CPU 是否 100%若是则检查是否有未优化的 SQL此时应开启 MySQL 慢查询日志抓出执行时间 500ms 的 SQL针对性加索引或重写逻辑。本文还有配套的精品资源点击获取
返回列表