1. 项目背景与核心需求
学生选课系统是高校教务管理中最基础也最关键的模块之一。作为计算机专业毕业设计的经典选题,它涵盖了从数据库设计到前后端交互的完整开发流程。我去年指导过3个类似项目,发现学生们最常遇到的痛点集中在并发选课处理、权限控制和数据一致性这几个方面。
这个基于Java的选课系统采用经典的MVC架构,后端使用Spring Boot框架,前端可选JSP或Thymeleaf模板引擎。数据库方面,MySQL 8.0提供了完善的事务支持,这对解决选课冲突至关重要。系统需要实现三类角色的完整功能闭环:
- 学生端:课程查询、选课退课、个人课表查看
- 教师端:开课申请、成绩录入、班级学生管理
- 管理员端:课程管理、用户管理、系统监控
2. 技术栈选型与架构设计
2.1 为什么选择Spring Boot
相比传统的SSM(Spring+SpringMVC+MyBatis)组合,Spring Boot的自动配置特性可以让开发者更专注于业务逻辑。实测在开发相同功能时,Spring Boot能减少约40%的样板代码。特别是在处理跨模块依赖时,starter机制避免了版本冲突问题。
java复制// 典型Controller示例
@RestController
@RequestMapping("/course")
public class CourseController {
@Autowired
private CourseService courseService;
@GetMapping("/list")
public Result listCourses(@RequestParam(required = false) String keyword) {
return Result.success(courseService.queryCourses(keyword));
}
}
2.2 数据库设计要点
选课系统的ER图核心是四张主表:
- 学生表(student):学号作为主键
- 教师表(teacher):工号为主键
- 课程表(course):包含课程容量字段
- 选课记录表(selection):学生与课程的关联表
特别注意:选课记录表需要建立联合唯一索引(student_id, course_id),防止重复选课。课程表应该设置version字段用于乐观锁控制并发。
sql复制CREATE TABLE selection (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
student_id VARCHAR(20) NOT NULL,
course_id BIGINT NOT NULL,
selection_time DATETIME DEFAULT CURRENT_TIMESTAMP,
score DECIMAL(5,2),
UNIQUE KEY uk_student_course (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES student(student_id),
FOREIGN KEY (course_id) REFERENCES course(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现细节
3.1 选课业务的事务处理
选课操作必须保证原子性:检查课程余量→创建选课记录→更新课程已选人数。这个过程中最容易出现并发问题。我们采用两种方案结合:
- 数据库层面:使用SELECT...FOR UPDATE悲观锁
- 应用层面:通过@Transactional注解声明事务
java复制@Transactional(rollbackFor = Exception.class)
public Result selectCourse(String studentId, Long courseId) {
// 1. 检查课程是否存在及是否已选
Course course = courseMapper.selectByIdForUpdate(courseId);
if(selectionMapper.existsSelection(studentId, courseId)) {
throw new BusinessException("不能重复选课");
}
// 2. 检查课程容量
if(course.getSelected() >= course.getCapacity()) {
throw new BusinessException("课程已满");
}
// 3. 创建选课记录
Selection selection = new Selection();
selection.setStudentId(studentId);
selection.setCourseId(courseId);
selectionMapper.insert(selection);
// 4. 更新已选人数
courseMapper.incrementSelected(courseId);
return Result.success("选课成功");
}
3.2 权限控制方案
采用RBAC(基于角色的访问控制)模型,结合Spring Security实现:
- 角色分为:ROLE_ADMIN, ROLE_TEACHER, ROLE_STUDENT
- 权限注解控制方法访问:@PreAuthorize("hasRole('TEACHER')")
- 前端菜单根据角色动态渲染
重要提示:永远不要在前端单独依赖角色判断来隐藏功能按钮,所有接口必须进行后端权限校验。这是毕设答辩时老师最常检查的安全点。
4. 典型问题与解决方案
4.1 选课超卖问题
在高并发选课时可能出现超卖(选课人数超过容量)。我们通过压力测试发现,单纯依赖数据库事务仍可能在极端情况下出现此问题。最终解决方案:
- 在课程表增加version字段实现乐观锁
- 使用Redis分布式锁控制选课入口
- 前端加入防重复提交机制
java复制// Redis锁示例
public boolean tryLock(String key, long expireTime) {
String value = String.valueOf(System.currentTimeMillis() + expireTime + 1);
if (redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = redisTemplate.opsForValue().get(key);
if (currentValue != null && Long.parseLong(currentValue) < System.currentTimeMillis()) {
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
return oldValue != null && oldValue.equals(currentValue);
}
return false;
}
4.2 性能优化实践
当课程列表数据量较大时(如超过1000门课),需要特别注意:
- 分页查询:使用PageHelper插件
- 缓存策略:热门课程信息存入Redis
- 数据库索引:为查询条件字段建立合适索引
yaml复制# MyBatis配置示例
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
cache-enabled: true
default-statement-timeout: 30
map-underscore-to-camel-case: true
5. 项目扩展与答辩准备
5.1 常见毕设扩展方向
基础功能完成后,可以考虑以下加分项:
- 选课冲突检测(时间冲突、先修课检查)
- 可视化图表统计(使用ECharts)
- 微信小程序端接入
- 基于Elasticsearch的课程搜索
5.2 答辩注意事项
根据我参与评审的经验,老师最关注:
- 数据库设计是否规范(范式、索引)
- 是否考虑了并发场景
- 代码分层是否清晰
- 是否有完整的测试用例
建议准备:
- 数据库ER图(使用PowerDesigner或Navicat生成)
- 压力测试报告(JMeter测试结果)
- 代码覆盖率报告(JaCoCo)
这个项目我已经将完整源码和部署文档整理在GitHub仓库,包含详细的注释和单元测试。在实际开发中,建议使用Git进行版本控制,每个功能点创建单独分支开发。遇到环境问题可以查看项目的pom.xml文件,里面已经配置好了所有依赖的稳定版本。
