ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于Java的学生选课系统设计:从需求分析到数据库实现

基于Java的学生选课系统设计:从需求分析到数据库实现

简介:一份基于Java的学生选课系统毕业设计论文文档,面向计算机相关专业毕业生及需要完成课设、毕设的开发者。内容围绕学生选课场景,系统梳理了项目背景与意义、用户角色与功能需求,涵盖课程管理、选课退选、成绩管理、通知公告及权限管理等模块,并介绍了基于JSP、jQuery、Java三层架构与MySQL数据库的技术选型及开发流程。论文还给出了需求规格、概要设计、详细设计、编码测试与部署上线的完整思路,并附有摘要、目录等规范格式,可作为论文写作与系统实现的参考范本。资源为1个doc文档,压缩包大小1.6MB,方便直接阅读或按需修改。已有462人学习,适合需要快速搭建论文框架、完善选课系统功能设计及了解JavaWeb项目开发流程的读者。

1. 学生选课系统的立项:从一场选课崩溃事故说起

每到选课季,教务办公室的打印机就没停过,几十个班的选课单收上来,靠Excel人工汇总,总有人超选学分、两人选同一门课,最后统计成绩时还要对着纸质单子一项项核。这个场景几乎是每所高校都经历过的“选课事故”。下面要讲的,是一套基于Java的学生选课系统设计方案:以某高校软件专业的毕业设计项目为原型,完成需求分析、三层架构设计、数据库设计,再到登录鉴权、选课事务、教师管理模块的完整落地。准备java毕设、期末课设或需要交完整论文的同学,都可以把这份方案当作参考骨架,先理解需求怎么拆,再讨论代码怎么写,最后清楚系统怎么验收。

2. 需求分析:三类角色、六组功能用例与选课数据流的拆解

2.1 用户角色与核心诉求:教师、学生、管理员的权限关系

这个系统最初的需求非常朴素:学生在期末选课前要能查课程、选课程,教师不用再抱着Excel核对。但随着讨论深入,角色边界就变得明确了。系统一共有三类使用者:学生、教师和管理员。把管理员单独拆开并不是增加一套页面,而是明确权限边界——教师负责日常业务操作,管理员负责账号和数据维护。在实际毕设里,我通常会把管理员权限合并进教师端,教师登录后就是最高权限,所有管理功能都在同一套页面里,这样既能满足“权限分级”的论证,又不至于把页面工作量翻倍。

从需求文档更细化的功能描述来看,三个角色的诉求大致如下。学生登录或验证身份后,可以自己完成选课和查询个人信息的任务,需要看到当前可选课程、已选课程,并能删除已选课程来调整选课计划。教师登录后,负责录入、修改、删除学生和课程信息,还要能按编号、名称等方式查询学生与课程,统计每个学生的选修学分,筛选出选修学分不足的学生。管理员则承担系统层面的维护工作,比如数据库备份、账号管理,这部分在单机部署的毕设场景里工作量不大,更多是把它作为安全性论证写在文档里。

我的建议是:角色需求先写清楚,再动代码。如果一上来就只盯着建表和CRUD,很容易把“管理员”做成摆设,最后论文里写三层角色,演示时却只演示了教师页面,答辩现场容易被追问。把这三个角色的操作边界列成一张表,论文和演示脚本都用它作为主线,后面的设计和实现都能对齐。

2.2 功能用例梳理:六组核心用例的参与者和流程

整理需求描述后,系统核心功能可以拆成六组用例:用户登录验证、课程信息管理、学生信息管理、选课管理、信息查询与模糊查询、学分汇总与筛选。

第一组是用户登录验证。登录页面提供教师和学生两种身份选择,用户输入编号和密码,系统在对应数据表中验证匹配关系;匹配成功则进入对应角色主页,匹配失败则回到登录页并提示错误。这里有一个容易被忽略的需求细节:不同的身份对应不同的数据表,教师在教师表里验证,学生在学生表里验证,而不是笼统地查一张用户表。这个设计会增加一张表的维护成本,但能更直观地体现角色边界。

第二组是课程信息管理。教师可以添加新课程、删除不需要的课程、修改已有课程的信息,并查询课程列表。每次增删改操作成功后,页面要给出明确提示;课程查询支持分页显示,避免一次加载太多记录。第三组是学生信息管理,教师对学生信息执行同样的增删改查操作,同样要求操作成功提示和分页显示。

第四组是选课管理,这是整个系统的核心。学生查看所有可选课程,选择未满员的课程进行选修;选课前系统要校验该学生是否已经选过这门课以及选课总学分是否超过上限,二者任一不满足则阻止选课。成功选课后,这门课程对该学生不再开放重复选课。第五组是信息查询,学生和教师都能根据学号、姓名、班级、课程名等条件查询选课情况,查询方式包括精确匹配和模糊匹配。

第六组是学分汇总与筛选。系统按学号汇总每个学生的选修学分,和“最低选修学分”标准比较,筛出学分不足的学生并标记。教师进入学分统计页面时,既能看全班汇总,也能设置阈值筛选不达标名单。这六组用例覆盖了需求分析里的全部功能点,毕业论文的第二章按这个顺序写,评审老师很容易找到你的功能列表和他预期功能的对应关系。

2.3 选课数据流与关键业务约束

数据流图是需求分析里的标准工具,毕业设计文档里通常会放一张上下文数据流图。这里不画图,只描述三条核心数据流,后面画图时照着这个结构画就行。

登录数据流:用户在登录页选择教师或学生身份,输入编号和密码,表单提交到Servlet,Servlet调用登录DAO查询对应的数据表;查询结果返回后,匹配成功就把用户身份写入Session并跳转主页,匹配失败则返回登录页并携带错误信息。这条链路的关键是Session,后续所有页面的权限判断都依赖Session里保存的role和编号。

选课数据流:学生进入可选课程列表,点击选课按钮提交课程编号,Servlet调用选课Service。Service先查选课表判断是否重复,再汇总已选学分判断是否超限,两项校验都通过才插入选课记录,并刷新已选课程列表。这条链路必须开启事务,否则插入失败时已经执行的校验结果无法回滚。

学分汇总数据流:教师打开学分统计页面,Servlet调用DAO执行分组汇总查询,将每个学生的学号、姓名和总学分返回给页面显示;页面提供一个“最低学分”输入框,服务端把低于阈值的学生单独标记出来。实现时最简单的方式是SQL里直接加HAVING条件。

业务约束方面,有四个必须在设计阶段定死的规则。第一,每个学生设有选修学分上限,选课时动态判断,超过上限立即提示且不能继续选课;第二,同一学生不能重复选择同一门课程;第三,学生不能直接“修改”已选课程,想调整必须先删除原选课记录再重新选课,需求文档里“选课后无法更改”就是用这个机制实现的;第四,每门课程在选课页展示授课教师、学分、上课地点等信息,课程未满员才允许选课。把四个约束写进需求文档后,后面数据库设计里的唯一索引和事务逻辑就都有了依据。

3. 总体架构设计:B/S结构、三层架构与模块职责划分

3.1 技术选型:JSP、jQuery与JavaWeb组合的理由

技术选型不需要追求新框架,而是要满足毕设环境的“可运行、可答辩、可说明”。这套系统采用B/S结构,用户在浏览器完成所有操作,服务端处理业务逻辑和数据库访问。开发工具使用IntelliJ IDEA,部署在Tomcat容器上,后端就是标准的JavaWeb工程,页面用JSP和jQuery实现,数据库用MySQL。之所以选这个组合,最重要的原因是它能把“三层架构”讲清楚:JSP页面属于表现层,Servlet和Service属于业务逻辑层,DAO属于数据访问层,每一层的职责在代码里都能找到对应的文件,答辩时被问到“改需求要动哪一层”,可以直接指出来。

选择jQuery而不是Vue或React,也有实际考虑。JSP页面在后端渲染,jQuery负责DOM操作和Ajax请求,学习成本低,代码量可控,而且嵌套在JSP里的jQuery代码更容易让评审老师看懂前后端交互过程。MySQL作为关系型数据库,稳定性好,SQL语法通用,导出建表语句就能完整展示数据库设计,这些对毕业设计都是加分项。如果团队里已经有人熟悉Spring Boot,用它替代Servlet也可以,但这里尽量保持原生JavaWeb,目的是把Servlet生命周期、请求转发、Session机制这些基础概念完整暴露出来,论文的“关键技术”章节才有内容可写。

3.2 三层架构:表现层、业务层、数据层的调用关系

三层架构是这个系统的骨架,我一般会在项目里严格按包名划分:web包放Servlet,service包放业务逻辑,dao包放数据库访问,entity包放实体类。调用关系固定为“JSP页面 → Servlet → Service → DAO → MySQL”,数据查询结果的返回路径则相反。

以“学生选课”请求为例:学生在course_list.jsp页面点击选课按钮,jQuery发起Ajax请求到选择课程Servlet,Servlet从请求中取出学号和课程号,传给选课Service;Service先做业务校验,再调用选课DAO的insert方法把选课记录写入MySQL;执行结果返回到Service,Service把结果返回给Servlet,Servlet把“成功/失败”的JSON文本写回页面,jQuery拿到结果后刷新已选课程列表。整个链条中,JSP页面不知道SQL长什么样,DAO也不关心页面跳转逻辑,每个类只要干好自己的事。

这种结构对后期维护特别友好。比如需求从“选课后不允许退选”变成“允许删除已选课程”,只需要在Service里增加一个删除课程方法,DAO里加一条DELETE语句,页面加一个按钮,三层各改一个文件就可以完成,不用翻整个项目找调用点。对论文来说,三层架构的麻烦之处在于类数量会多一些,但好处是每个类的职责单一,可以按层分别写详细设计,这一条在“系统模块详细设计”章节里非常好展开。

3.3 功能模块划分与页面流转关系

总体设计阶段的模块划分,可以直接复用需求分析里梳理的功能点。用户验证登录模块负责登录页的身份选择、密码验证和Session处理;教师课程操作模块负责课程信息的增删改查;教师学生操作模块负责学生信息的增删改查;模糊查询模块提供按学号、姓名、班级、课程名等条件的组合查询;学分控制模块负责校验选课学分上限和统计低于阈值的学生;学生选课模块负责查看课程、选课、退课和查看已选课程。

页面流转关系大概是这样:登录页面是唯一入口,登录成功后按角色分发——教师进入教师主页,学生进入学生主页。教师主页提供学生管理、课程管理、学分统计三个入口,分别对应学生列表页面、课程列表页面和学分统计页面;学生主页提供可选课程、已选课程两个页面。所有管理页面都带分页和查询条件区域,操作结果通过页顶提示信息反馈。这套页面结构很简单,但已经覆盖了六组用例的所有操作场景,演示的时候从头点到尾,不需要临时切换复杂页面。

4. 数据库设计:四张核心表的字段规划与学分汇总查询

4.1 实体分析:教师、学生、课程与选课记录

数据库设计从实体分析开始。根据系统需求,可以确定四个核心实体:教师、学生、课程和选课关系。

教师实体包含教师编号、密码、姓名三个属性,编号是唯一标识;学生实体包含学号、姓名、密码、班级、性别;课程实体包含课程编号、课程名称、课程学分,授课教师信息也挂在课程实体下。选课关系是一个典型的联系实体,记录学号、课程编号、课程学分、上课地点和成绩,因为一个学生可以选择多门课程,一门课程可以被多个学生选修,学生和课程是多对多关系,必须通过选课表把这个多对多拆成两个一对多。

E-R图里比较值得讨论的是授课教师字段。有些设计会把教师拆成独立表再通过编号关联课程,但在这个小系统里,课程表直接保存教师姓名就够用,教师表单独维护账号和密码即可。这样的设计减少了表间关联,演示和论文都更容易解释。如果后续需求扩展到“按教师查课程安排”,再把教师编号加进课程表并建立外键,属于可扩展点。

4.2 数据表物理设计:建表SQL与字段约束

逻辑结构确定后,转成MySQL物理表结构。下面是四张核心表的建表SQL,字段命名尽量和需求文档保持一致:

CREATE TABLE teacher ( tea_no VARCHAR(11) PRIMARY KEY, tea_name VARCHAR(20) NOT NULL, tea_password VARCHAR(20) NOT NULL ); CREATE TABLE student ( stu_no VARCHAR(11) PRIMARY KEY, stu_password VARCHAR(20) NOT NULL, stu_name VARCHAR(20) NOT NULL, stu_class VARCHAR(20), stu_sex VARCHAR(2) ); CREATE TABLE course ( course_no VARCHAR(11) PRIMARY KEY, course_name VARCHAR(50) NOT NULL, teacher VARCHAR(20) NOT NULL, credit DECIMAL(4,1) NOT NULL ); CREATE TABLE sc ( id INT AUTO_INCREMENT PRIMARY KEY, stu_no VARCHAR(11) NOT NULL, course_no VARCHAR(11) NOT NULL, credit DECIMAL(4,1) NOT NULL, classroom VARCHAR(20), score DECIMAL(5,1), UNIQUE KEY uk_stu_course (stu_no, course_no), CONSTRAINT fk_sc_student FOREIGN KEY (stu_no) REFERENCES student(stu_no) ON DELETE CASCADE, CONSTRAINT fk_sc_course FOREIGN KEY (course_no) REFERENCES course(course_no) ON DELETE CASCADE );

逐表说明:teacher表的tea_no作为登录账号,主键保证唯一;student表单独存密码,stu_no作为登录账号;course表里teacher直接保存授课教师姓名,credit用decimal(4,1)而不是float,因为浮点数做累加时可能出现精度误差,学分是0到50之间的数值,用decimal既精确又直观。

sc表是选课关系表,也是整个系统最关键的物理设计。id是自增主键,stu_no和course_no共同组成联合唯一索引uk_stu_course,这保证同一学生同一课程在数据库层面无法重复插入。credit字段从course表冗余过来,避免选课后课程表修改学分导致历史记录失真;classroom记录上课地点,score允许为空,教师后续录入成绩。外键在删除学生或课程时级联删选课记录,避免出现“课程已删但选课列表还残留”的孤儿数据。

4.3 学分汇总SQL与选课记录设计

学分汇总功能依赖两处SQL:一是查询某个学生的已选总学分,二是筛选学分不足的学生。查询单个学生已选学分的SQL如下:

SELECT COALESCE(SUM(credit), 0) AS total_credit FROM sc WHERE stu_no = ?;

COALESCE(SUM(credit), 0)的作用是当学生没有任何选课记录时返回0而不是NULL,这样Java代码里不需要做额外的空值判断。筛选选修学分不足学生的SQL则是分组求和后和阈值比较:

SELECT s.stu_no, s.stu_name, SUM(sc.credit) AS total_credit FROM student s LEFT JOIN sc ON s.stu_no = sc.stu_no GROUP BY s.stu_no, s.stu_name HAVING total_credit < ?;

这里用LEFT JOIN而不是JOIN,是因为没有选课记录的学生也要出现在统计结果里,他们的总学分是0,理应被标记为学分不足。HAVING子句在分组后过滤,比WHERE更符合这个场景的语义。教师页面传入一个最低学分阈值,比如20,这条SQL就返回所有选修总学分低于20的学生名单。

选课记录插入时,credit是从course表补查出来再写入sc表的,而不是让前端直接传学分。这样做可以防止页面提交伪造数据。成绩字段score初始为NULL,教师后续通过成绩录入功能更新,不参与选课阶段任何校验。

5. 模块实现与常见问题排查:登录鉴权、选课事务与五个翻车点

5.1 登录模块:身份选择、密码匹配与Session存储

登录模块的实现重点是两个:一是不同身份查不同表,二是密码验证防止SQL注入。我习惯用PreparedStatement写数据库访问,参数用“?”占位符先预编译再传值,这样无论输入什么内容,都会被当作参数而不是SQL片段,是最基本的防注入手段。

登录DAO的核心代码可以写成下面这样:

public User login(String role, String no, String password) { User user = null; String sql = null; if ("teacher".equals(role)) { sql = "SELECT tea_no AS no, tea_name AS name FROM teacher" + " WHERE tea_no = ? AND tea_password = ?"; } else if ("student".equals(role)) { sql = "SELECT stu_no AS no, stu_name AS name FROM student" + " WHERE stu_no = ? AND stu_password = ?"; } else { return null; } try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, no); ps.setString(2, password); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { user = new User(role, rs.getString("no"), rs.getString("name")); } } } catch (SQLException e) { e.printStackTrace(); } return user; }

这段代码里有两个设计点值得在答辩时提。第一,SELECT后面用AS把字段重命名为统一的no和name,这样教师和学生表虽然列名不同,但读取方式一致,简化业务层代码;第二,登录成功只带回编号和姓名,不把密码放进User对象,减少敏感数据在Session里的暴露。Servlet拿到User对象后,把role和no存入Session,后续所有页面的权限判断都从这里取。

5.2 学生选课模块:事务控制与双重防重设计

选课是整个系统业务逻辑最复杂的地方,也是最容易被追问“并发怎么办”的模块。实现思路是:开启事务,查课程是否有效,查学生是否已选,查总学分是否超限,全部通过则插入选课记录并提交;任何一个环节失败都回滚,保证选课不产生半条脏数据。

下面是一段可运行的Service层核心逻辑:

public boolean chooseCourse(String stuNo, String courseNo, String classroom) { final double MAX_CREDIT = 25.0; Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); PreparedStatement psCourse = conn.prepareStatement( "SELECT credit FROM course WHERE course_no = ?"); psCourse.setString(1, courseNo); ResultSet rsCourse = psCourse.executeQuery(); double courseCredit = 0; if (rsCourse.next()) { courseCredit = rsCourse.getDouble("credit"); } else { conn.rollback(); return false; } PreparedStatement psCheck = conn.prepareStatement( "SELECT id FROM sc WHERE stu_no = ? AND course_no = ? FOR UPDATE"); psCheck.setString(1, stuNo); psCheck.setString(2, courseNo); if (psCheck.executeQuery().next()) { conn.rollback(); return false; } PreparedStatement psSum = conn.prepareStatement( "SELECT COALESCE(SUM(credit), 0) FROM sc WHERE stu_no = ?"); psSum.setString(1, stuNo); ResultSet rsSum = psSum.executeQuery(); double selectedCredit = rsSum.next() ? rsSum.getDouble(1) : 0; if (selectedCredit + courseCredit > MAX_CREDIT) { conn.rollback(); return false; } PreparedStatement psInsert = conn.prepareStatement( "INSERT INTO sc (stu_no, course_no, credit, classroom, score)" + " VALUES (?, ?, ?, ?, NULL)"); psInsert.setString(1, stuNo); psInsert.setString(2, courseNo); psInsert.setDouble(3, courseCredit); psInsert.setString(4, classroom); psInsert.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { if (conn != null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

这段代码值得逐段展开。查课程信息用第一个PreparedStatement,课程不存在直接回滚;防重查选用第二个PreparedStatement并加FOR UPDATE,它的作用是把这条选课记录的行锁锁定,避免两个并发请求同时通过“未选过”校验。第三个查询汇总已选学分,selectedCredit加上本次课程学分超过25就回滚。MAX_CREDIT定义为25.0,实际项目中这个值可以从学生基本信息表读取,这里用常量是便于演示。

为什么有了联合唯一索引还要用FOR UPDATE?因为联合唯一索引只能保证“插入时不产生重复记录”,但无法阻止两个请求都在“查询阶段”看到空结果并同时进入插入流程,届时一个请求会因唯一键冲突失败,另一个成功。FOR UPDATE则是把并发问题提前到查询阶段,两个请求的时序被排列,业务逻辑能给出明确的“重复选课”提示,而不是抛出底层异常。答辩时能把这两个层次的防重讲清楚,这块的分数基本就稳了。

5.3 教师管理模块:CRUD、分页查询与删除级联

教师管理模块包括两个实体的增删改查:学生信息和课程信息。两者在实现上几乎对称,差别只在字段不一样。通用做法是:列表页加载时通过分页查询显示第一页数据,查询表单提交后把条件拼进LIMIT子句;新增和编辑共用一个表单页面,后端根据主键是否存在决定执行INSERT还是UPDATE;删除按钮提交主键,后端执行DELETE。

分页查询的SQL可以写成:

SELECT * FROM student WHERE stu_name LIKE CONCAT('%', ?, '%') OR stu_no LIKE CONCAT('%', ?, '%') ORDER BY stu_no LIMIT ?, ?;

这里LIMIT第一个参数是偏移量,第二个参数是每页条数,比如每页10条,第3页就是从偏移20开始取10条。LIKE配合CONCAT构造模糊条件,两个问号都传同一个关键词。如果查询条件为空字符串,CONCAT('%', '', '%')会生成'%%',在MySQL里能匹配所有记录,所以空条件时这段WHERE实际上不产生拦截效果。由于用了PreparedStatement,这个查询天然免疫SQL注入。

删除课程时,课程表与选课表存在外键关联,建表时已经配置ON DELETE CASCADE,所以删除课程记录时,sc表里对应的选课记录会被MySQL自动清理。这在演示时是一个很好的“数据完整性”证据,不需要写额外的清理代码。

5.4 避坑指南:选课系统最容易翻车的五个点

第一个翻车点:学分汇总出现浮点误差。现象是学生明明选了刚好等于上限的学分,系统却提示超过上限,或者成绩统计里出现5.1999999这样的数据。原因是Java的double和MySQL的float无法精确表示部分十进制小数。解决方法是把数据库字段改成DECIMAL类型,Java侧用BigDecimal参与累加,或者在比较时保留两位小数。选课系统里用DECIMAL(4,1)已经能覆盖绝大多数场景。

第二个翻车点:重复选课。现象是同一个学生同一门课程出现在选课记录表两行。原因通常有两个:代码里只做了查询校验没建唯一索引,以及两个选课请求并发到达。解决方法是双管齐下:建表时加UNIQUE KEY uk_stu_course(stu_no, course_no)作为最终防线,业务层再配合FOR UPDATE查询,双重重防护。

第三个翻车点:删除课程后出现孤儿数据。现象是学生已选列表里显示一门课程名称为空或查不到的课程,学分汇总也把这门课算进去。原因是删除课程时没有处理选课表的外键关联。解决方法是外键配置ON DELETE CASCADE,或者在删除逻辑里先删sc表记录再删course表记录,推荐前者,数据库层自动保证一致性。

第四个翻车点:SQL注入。现象是登录页面输入“1' OR '1'='1”这类内容后,返回了用户信息。原因是字符串拼接SQL参数,把输入内容当成了SQL片段执行。解决方法是全部改用PreparedStatement,参数用“?”占位,养成习惯后基本不会再犯。这条在任何答辩里都是必问项。

第五个翻车点:修改课程学分后选课汇总数据跟当前课程不一致。现象是课程表把某门课从3学分改成2学分,但历史选课记录仍然按3学分统计。原因是选课表冗余了credit字段作为业务快照,但修改课程学分时没有同步更新sc表。解决方法是明确“快照”语义:选课表的credit代表选课那一刻的学分,不做同步;如果希望在改学分后统一调整历史选课记录,就需要在课程更新逻辑里额外执行一条UPDATE sc SET credit = ? WHERE course_no = ?。这取决于业务规则,但不要“既不更新又假装它一致”。

6. 系统验证与答辩准备:测试用例、演示顺序与讲解主线

6.1 验证方案:一张覆盖核心场景的功能测试表

系统做完后,验证环节不能只靠“点一遍没报错”。我习惯先列一张功能测试用例表,把每个核心场景的操作和预期结果写清楚,再按表逐行执行,记录实际结果。下面这张表可以直接拿来做验收清单:

测试场景操作预期结果
登录成功选择学生身份,输入正确学号密码进入学生主页
登录失败输入错误密码提示错误并返回登录页
选课成功选择未选且学分不超限的课程选课记录插入,已选列表显示该课程
学分超限拦截已选学分加上课程学分超过上限提示超过上限,不插入记录
重复选课拦截对已选课程再次提交选课提示已选过该课程,不插入记录
退选课程在已选列表删除一门课程记录删除,可重新选择该课程
教师增删改查新增/修改/删除学生或课程操作成功提示,列表刷新
学分汇总筛选设置最低学分阈值查询只显示低于阈值的学生名单

6.2 答辩演示顺序与讲解主线

演示顺序建议按“需求主线”走:先打开登录页,分别演示教师和学生两种身份的登录差异;进入教师端,演示课程信息维护和学生信息维护,随手改一条数据,让评审看到提示和列表刷新;再切到学生端,演示一次正常选课、一次超学分拦截、一次重复选课拦截;最后回到教师端,打开学分统计页,输入阈值筛选出学分不足的学生。整个流程控制在8分钟以内,重点展示“拦截”和“汇总”这两个非CRUD功能,它们才是这个系统和普通增删改查作业的关键区别。

讲解时有三大主线可以用。第一是数据流主线:从页面提交到Servlet再到DAO,说明数据是怎么在系统里走的;第二是架构主线:用三层架构的说法解释每一层的职责,强调业务校验放在Service而不是页面;第三是安全与数据完整性主线:主动讲PreparedStatement防注入、联合唯一索引防重复、外键级联保证不产生孤儿数据。

我这里有一个血泪经验。当年做这个系统时,有位评审老师只问了一句“两个学生同时选同一门课,你的表里会插几条记录”,我当时没答上来,因为代码里只有查询校验,没建唯一索引,也没用FOR UPDATE。从那以后,我每次答辩前都会先把表约束和事务边界过一遍,确认“两个并发请求同时到达”这个场景能一句话讲清楚,再演示功能。希望帮到你。

本文还有配套的精品资源,点击获取

返回列表