ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

学生选课系统源码拆解:SpringBoot+Vue+MySQL完整落地路径

2026/10/1 3:19:08 拓冰建站 浏览量
学生选课系统源码拆解:SpringBoot+Vue+MySQL完整落地路径 简介本资源为基于SpringBootVueMySQL的学生选课系统毕业设计完整源码包面向计算机相关专业需要完成毕业设计或课程设计的学生以及希望学习前后端分离开发与数据库管理的Java初学者。压缩包共126个文件约3.83MB以97个Java源文件为核心辅以bat启动脚本、js与css前端资源、xml与yml配置、sql数据库脚本以及docx开题报告、doc说明文档和ppt答辩展示等资料覆盖从编码到答辩的完整链路。系统实现管理员管理专业、教师、学生与课程教师审核选课并录入成绩学生选课与查询成绩等核心流程采用SpringBootMyBatis后端与Vue前端分离架构JDK1.8、MySQL5.7环境可直接部署运行。已有61人学习下载适合作为毕业设计参考模板与Java全栈入门实践案例。1. 学生选课系统源码拆解从 SpringBoot 到 Vue 的完整落地路径每年毕业季计算机专业的同学都会面对同一个问题选什么题目既有技术含量又能在一个月内跑通并写出论文。学生选课系统源码SpringBootVueMySQL说明文档LW这个组合恰好踩中了 Java 毕业设计最主流的三个技术栈——SpringBoot 做后端、Vue 做前端、MySQL 做数据存储再配一份说明文档和论文LW 即论文的拼音缩写。它解决的核心问题是让你不用从零搭架子直接拿到一套可运行、可改、可写论文的完整工程。这套方案适合谁适合 Java 基础刚学完、SpringBoot 还没完整做过一个项目、Vue 只会写几个组件的同学。你拿到源码后需要做的不是“打开就能交”而是理解每一层怎么串起来、数据库怎么设计、前后端怎么联调然后在此基础上改出属于自己的东西。直接交源码的后果答辩时老师问一句“你这个选课冲突怎么判断的”就能让你哑口无言。所以这篇内容的目标很明确把这份源码背后的技术栈拆开告诉你每一步怎么跑通、参数怎么配、坑在哪让你真正能改、能讲、能写。2. 环境搭建与项目启动把源码跑起来的第一公里2.1 JDK、Maven、Node 的版本选择与安装拿到源码后第一件事不是打开 IDE而是确认本机环境。SpringBoot 项目对 JDK 版本有硬性要求Vue 项目对 Node 版本同样敏感。常见做法是JDK 用 1.8 或 11Maven 用 3.6 以上Node 用 14.x 或 16.x。为什么不用最新版因为 SpringBoot 2.x 在 JDK 17 上虽然能跑但部分依赖比如旧版 Lombok会报错Node 18 以上对 Vue2 的 node-sass 兼容性很差安装依赖时容易卡在编译阶段。安装 JDK 后配置JAVA_HOMEMaven 配置MAVEN_HOME和本地仓库路径。Node 安装完成后建议用nrm切换到国内镜像源否则npm install会慢到让你怀疑人生。验证命令如下java -version mvn -v node -v npm -v四个命令都能输出版本号说明基础环境就绪。如果mvn -v报错检查MAVEN_HOME是否指向 Maven 解压目录而非 bin 目录如果npm -v报错检查 Node 安装时是否勾选了“Add to PATH”。注意不要同时装多个 JDK 版本而不配置JAVA_HOME否则 Maven 编译时会用到系统默认的那个导致版本不匹配。2.2 MySQL 建库与数据导入的完整流程源码里通常会带一个.sql文件这是数据库的“后悔药”——只要导入成功表结构和初始数据就都有了。常见做法是先用 Navicat 或命令行创建数据库字符集选utf8mb4排序规则选utf8mb4_general_ci。为什么不用utf8因为utf8在 MySQL 里是“假 UTF-8”存不了 emoji 和部分生僻字选课系统的课程名、学生姓名里万一有生僻字就会乱码。创建数据库的命令CREATE DATABASE course_selection DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;然后导入 SQL 文件mysql -u root -p course_selection course_selection.sql导入完成后用SHOW TABLES;确认表是否齐全。一个典型的学生选课系统至少包含学生表student、教师表teacher、课程表course、选课记录表course_selection、用户表user。如果少了某张表说明 SQL 文件不完整需要检查源码里的实体类反推缺失的表结构。提示导入前先看 SQL 文件头部有没有CREATE DATABASE语句。如果有直接执行整个文件即可如果没有必须先手动建库再导入。2.3 后端配置文件修改与启动验证SpringBoot 项目的配置文件通常是application.yml或application.properties位置在src/main/resources下。需要改的核心项只有三个数据库连接、端口、文件上传路径。数据库连接要改成你本机的地址、用户名和密码spring: datasource: url: jdbc:mysql://localhost:3306/course_selection?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver server: port: 8080serverTimezoneAsia/Shanghai这个参数不能省否则 MySQL 8.x 会报时区错误。改完后在项目根目录执行mvn spring-boot:run或者在 IDE 里直接运行启动类。看到控制台输出Started Application in x.x seconds就算成功。如果报Access denied for user检查用户名密码如果报Unknown database检查数据库名是否拼错如果报Table xxx doesnt exist回到上一步确认 SQL 是否导入完整。2.4 前端依赖安装与联调配置前端项目通常在vue或frontend目录下先执行npm install。如果卡在node-sass编译换成sassDart Sass即可命令是npm uninstall node-sass npm install sass。安装完成后找到vue.config.js或.env.development把后端接口地址改成http://localhost:8080。启动命令是npm run serve浏览器打开http://localhost:8081端口可能不同看控制台输出。联调时最常见的翻车场景是跨域。前端 8081 调后端 8080浏览器会拦。解决办法有两种后端加CrossOrigin注解或者前端配代理。推荐前端配代理在vue.config.js里加devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } }这样前端请求/api/student/list会被代理到http://localhost:8080/student/list跨域问题就绕过去了。改完代理要重启npm run serve热更新不会让代理配置生效。3. 选课系统核心业务逻辑从表结构到接口实现3.1 数据库表设计的三个关键约束学生选课系统的表结构看着简单但有几个约束如果没设计好后面写业务逻辑会非常痛苦。第一个约束是选课记录表的唯一索引同一个学生不能重复选同一门课。常见做法是在course_selection表上建联合唯一索引ALTER TABLE course_selection ADD UNIQUE KEY uk_student_course (student_id, course_id);这样即使前端重复提交数据库层也会拦下来不用完全依赖后端判断。第二个约束是课程容量course表里要有max_students字段选课时先查已选人数再决定是否放行。第三个约束是选课时间窗口course表里加select_start和select_end两个时间字段后端在选课前校验当前时间是否在窗口内。这三个约束分别对应选课系统的三个核心业务规则不能重复选、不能超容量、不能超时间。很多同学拿到源码后只改前端页面不动数据库结果答辩时被问“并发选课怎么保证不超卖”就卡住了。其实答案就在唯一索引和事务里。3.2 选课接口的事务与并发处理选课接口是整个系统最核心的写操作必须加事务。伪代码逻辑是开启事务 → 查课程是否存在 → 查是否已选 → 查已选人数是否小于容量 → 查是否在选课时间内 → 插入选课记录 → 更新课程已选人数 → 提交事务。任何一步失败都回滚。Transactional(rollbackFor Exception.class) public Result selectCourse(Long studentId, Long courseId) { Course course courseMapper.selectById(courseId); if (course null) return Result.fail(课程不存在); int count selectionMapper.countByStudentAndCourse(studentId, courseId); if (count 0) return Result.fail(已选过该课程); int selected selectionMapper.countByCourse(courseId); if (selected course.getMaxStudents()) return Result.fail(课程已满); Date now new Date(); if (now.before(course.getSelectStart()) || now.after(course.getSelectEnd())) return Result.fail(不在选课时间内); selectionMapper.insert(studentId, courseId); courseMapper.incrementSelected(courseId); return Result.success(选课成功); }Transactional注解保证插入选课记录和更新课程人数要么都成功要么都失败。但光有事务还不够高并发下两个请求同时查到“已选 49 人容量 50”都判断通过然后都插入结果变成 51 人。解决办法是在incrementSelected的 SQL 里加条件UPDATE course SET selected selected 1 WHERE id #{id} AND selected max_students;然后判断受影响行数是否为 1如果不是就抛异常回滚。这样即使并发数据库行锁也能保证不会超卖。3.3 退课与课程容量回滚退课逻辑比选课简单但容易漏掉容量回滚。正确做法是删除选课记录 → 课程已选人数减一。同样要加事务并且减一操作要判断selected 0防止减成负数。Transactional(rollbackFor Exception.class) public Result dropCourse(Long studentId, Long courseId) { int deleted selectionMapper.deleteByStudentAndCourse(studentId, courseId); if (deleted 0) return Result.fail(未选该课程); courseMapper.decrementSelected(courseId); return Result.success(退课成功); }decrementSelected的 SQL 写成UPDATE course SET selected selected - 1 WHERE id #{id} AND selected 0。退课时间窗口通常和选课窗口一致如果选课结束后不允许退课就在退课前加时间校验。3.4 课程列表查询与分页课程列表是读操作但数据量大时也要分页。常见做法是用 MyBatis-Plus 的分页插件或者手写LIMIT。前端传page和size后端返回total和records。查询条件包括课程名模糊搜索、教师名筛选、课程类型筛选。public PageCourseVO listCourses(int page, int size, String keyword, Long teacherId) { PageCourse p new Page(page, size); LambdaQueryWrapperCourse wrapper new LambdaQueryWrapper(); if (StringUtils.hasText(keyword)) wrapper.like(Course::getName, keyword); if (teacherId ! null) wrapper.eq(Course::getTeacherId, teacherId); wrapper.orderByDesc(Course::getCreateTime); return courseMapper.selectPage(p, wrapper); }分页查询要注意索引course表的name字段如果经常模糊搜索可以加普通索引teacher_id加索引加速筛选。但不要滥用索引写操作频繁的表加太多索引会拖慢插入速度。4. 前后端联调与权限控制让系统真正能用4.1 登录鉴权与 Token 传递学生选课系统通常有三种角色学生、教师、管理员。登录接口验证用户名密码后生成一个 Token 返回给前端。前端把 Token 存到localStorage每次请求通过请求头Authorization带上。后端用拦截器或过滤器校验 Token解析出用户 ID 和角色存入ThreadLocal供后续业务使用。public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { String token request.getHeader(Authorization); if (token null || !jwtUtil.validate(token)) { response.setStatus(401); return false; } Long userId jwtUtil.getUserId(token); String role jwtUtil.getRole(token); UserContext.set(userId, role); return true; }Token 用 JWT 生成密钥写在配置文件里过期时间设 2 小时。前端在axios拦截器里统一加请求头收到 401 就跳转登录页。4.2 角色菜单与接口权限不同角色看到的菜单不同学生看到“选课”“我的课程”教师看到“我的课程”“学生名单”管理员看到“课程管理”“用户管理”。前端用动态路由根据登录返回的角色生成菜单。后端接口也要加角色校验比如“添加课程”只允许管理员和教师调用。PreAuthorize(hasAnyRole(ADMIN,TEACHER)) PostMapping(/course/add) public Result addCourse(RequestBody Course course) { ... }PreAuthorize是 Spring Security 的注解需要在配置类里开启EnableGlobalMethodSecurity(prePostEnabled true)。如果源码里没有用 Spring Security也可以手写一个注解加拦截器实现原理一样。4.3 前端页面与接口字段对齐前后端联调最耗时的不是写代码而是字段对不上。后端返回{code: 200, data: {...}, msg: 成功}前端却按{status: 200, result: {...}}解析页面就是空白。解决办法是先把接口文档写清楚或者直接用 Postman 调一遍后端接口确认返回结构再写前端。常见做法是封装一个统一的请求函数async function request(url, options {}) { const res await axios({ url, ...options }); if (res.data.code ! 200) { throw new Error(res.data.msg || 请求失败); } return res.data.data; }这样业务代码里只关心data不用每次判断code。如果后端返回结构不统一先改后端别在前端到处写兼容逻辑。5. 避坑与排查源码跑不起来时先看这几条5.1 启动报错“找不到符号”或“程序包不存在”现象mvn spring-boot:run或 IDE 启动时编译失败提示某个类找不到。原因通常是 Maven 依赖没下载完整或者 JDK 版本不匹配。解决先执行mvn clean install -U强制更新依赖如果还报错检查pom.xml里的 Java 版本是否和本机一致。常见坑是pom.xml写的是 Java 11本机装的是 Java 8改pom.xml或换 JDK 都行。5.2 前端 npm install 卡在 node-sass现象npm install跑了十分钟不动最后报node-sass编译错误。原因node-sass和 Node 版本强绑定Node 16 以上基本装不上。解决卸载node-sass安装sass然后把package.json里的node-sass替换成sass。如果项目里用了sass-loader也要确认版本兼容sass-loader10.x 配sass没问题。5.3 数据库连接报“Public Key Retrieval is not allowed”现象后端启动时报com.mysql.cj.jdbc.exceptions.CommunicationsException: Public Key Retrieval is not allowed。原因MySQL 8.x 默认用caching_sha2_password认证插件JDBC 连接需要允许公钥检索。解决在 JDBC URL 后面加allowPublicKeyRetrievaltrue完整 URL 变成jdbc:mysql://localhost:3306/course_selection?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。5.4 选课接口返回成功但数据库没数据现象前端提示“选课成功”但刷新页面后课程还在选课记录表里没有新数据。原因事务没生效或者 MyBatis 的insert没提交。解决检查启动类有没有EnableTransactionManagementSpringBoot 自动开启一般不用加检查insert方法有没有被Transactional注解的方法调用。如果是在同类内部方法调用Transactional会失效因为 Spring AOP 代理不生效。把选课逻辑抽到单独的 Service 类里或者用AopContext.currentProxy()调用。5.5 前端页面空白但控制台无报错现象登录后跳转到主页页面一片空白F12 控制台没有红色报错。原因路由配置错误或者组件加载失败但被try-catch吞了。解决先看 Network 面板确认接口是否返回数据再看 Vue Devtools确认组件树是否渲染。常见坑是路由path写错比如/home写成了/hone或者router-view没放在正确位置。如果用了动态路由检查权限判断逻辑是否把所有路由都过滤掉了。6. 从能跑到能答辩源码改造与论文写作的衔接技巧源码跑通只是第一步答辩老师真正关心的是“你做了什么”。我的习惯是拿到源码后先做三件事画一张系统架构图、整理一份数据库表关系图、列出三个可以改进的点。架构图用 draw.io 画分层写清楚 Controller、Service、Mapper、Entity 的职责表关系图用 Navicat 的反向工程功能自动生成然后手动标注外键关系改进点从性能、安全、体验三个角度各找一个比如“选课接口加 Redis 缓存减少数据库压力”“密码用 BCrypt 加密存储”“前端加 loading 状态提升体验”。论文写作时不要照抄源码里的说明文档。说明文档是给开发者看的论文是给答辩老师看的。论文的“系统设计”章节要写清楚为什么这么设计比如“选课记录表加联合唯一索引是为了防止重复选课同时在数据库层保证数据一致性”。论文的“系统实现”章节要贴关键代码但不要贴全部选课接口贴事务和并发处理那一段就够了其他用文字描述。如果你想让系统更有亮点可以加一个“选课冲突检测”功能同一时间段只能选一门课。实现方式是在course表加section字段比如“周一1-2节”选课时查该学生已选课程里有没有相同section的。这个功能代码量不大但答辩时能讲出“时间冲突算法”比单纯增删改查有说服力。最后一个建议改源码时用 Git 管理每改一个功能提交一次。这样改崩了可以回滚答辩前也能看到自己到底改了哪些地方。我见过太多同学改到最后不知道哪行代码是自己写的答辩时被问“这个功能是你加的吗”只能含糊其辞。从跑通到能答辩中间隔的不是代码量而是你对每一行代码的理解。希望帮到你。本文还有配套的精品资源点击获取