学生选课系统数据库课设:从ER建模到JDBC事务实战

发布时间:2026/10/9 20:08:55

学生选课系统数据库课设:从ER建模到JDBC事务实战 简介本资源是一套完整的数据库课程设计实践项目——学生选课信息管理系统面向高校计算机、软件工程等专业本科生适用于数据库原理、Java程序设计、Web开发等课程的期末大作业与综合实训。系统采用Java语言开发后端基于Spring MVC框架前端使用HTMLCSSJS数据库为MySQL功能覆盖学生、教师、管理员三类角色的选课管理、课程查询、成绩录入与系统维护代码结构清晰、注释详尽新手可快速理解并部署运行。压缩包共278个文件含40个核心Java源码、87个XML配置与映射文件、26个HTML页面、20个JS交互脚本及12个CSS样式文件整体大小仅2.75MB轻量易用。目前已有637人学习下载资源包含完整可运行的源代码、建库SQL脚本、编译后的Class文件及基础静态资源目录模块划分明确如AccountController、XuankeInfoService、TeacherInfoController等便于分层学习与二次开发。1. 为什么一个“学生选课信息管理系统”能成为数据库课设的硬通货——它不是Demo而是关系型建模的最小闭环实战场你手头正赶着数据库课程设计 deadline老师要求“体现完整性、规范性、可运行性”而网上搜到的所谓“源码数据库”九成是字段乱序的 SQL 文件、没主外键约束的表结构、Java 代码里写满String sql select * from student where id id的拼接式黑匣子。这类项目跑得起来但一改就崩一查就懵根本经不起答辩追问。真正值得复用的课设必须同时满足三个刚性条件ER 图能闭环推导出表结构、MySQL 脚本含完整约束与索引、Java 层严格分层且 DAO 与业务逻辑解耦。这个标题指向的方案正是某高校数据库教学组连续五年迭代的“最小可行课设模板”它用最朴素的 Java SE JDBC 实现不依赖 Spring Boot 等框架强制你亲手写连接池、手写事务边界、手动处理外键级联异常——所有“玄学报错”都暴露在阳光下。适合两类人一是想拿高分又怕踩坑的本科生二是想夯实底层数据流意识的转行者。它不炫技但每行代码都在回答“为什么这里要加 ON DELETE CASCADE”“为什么 service 层不能直接 new DAO”。2. 从 ER 图到 MySQL 表如何把“学生-课程-选课”三元关系落地为带约束的物理表2.1 先画清楚 ER 图别跳过这步否则后面全是后悔药很多同学直接开建表结果发现“一个学生选多门课、一门课被多个学生选”这种多对多关系硬生生拆成两张一维表最后靠 Java 循环查来查去。正确起点是明确三张实体表student,course,teacher和一张关联表selection。关键细节student和course之间无直接属性必须通过selection关联teacher与course是一对多一个老师教多门课所以course表中设teacher_id外键selection表主键必须是联合主键(student_id, course_id)而非自增 ID —— 这是防止同一学生重复选同一门课的第一道防线。提示用 draw.io 或 PPT 手绘 ER 图时务必标清基数1..*、参与约束total/partial和弱实体如selection依赖student和course存在。这张图就是你后续建表的宪法改表先改图。2.2 MySQL 建库脚本约束不是装饰是数据质量的守门员以下脚本已在 MySQL 8.0 实测通过所有外键均启用ON UPDATE CASCADE ON DELETE CASCADE确保数据一致性不靠 Java 层补救-- 创建数据库并设置字符集 CREATE DATABASE IF NOT EXISTS student_selection_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_selection_system; -- 学生表学号为主键姓名非空邮箱唯一 CREATE TABLE student ( student_id VARCHAR(12) PRIMARY KEY COMMENT 学号如20230001, name VARCHAR(20) NOT NULL COMMENT 姓名, gender ENUM(M, F) DEFAULT M COMMENT 性别, email VARCHAR(50) UNIQUE COMMENT 邮箱用于登录验证, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB COMMENT学生基本信息表; -- 教师表工号为主键职称有默认值 CREATE TABLE teacher ( teacher_id VARCHAR(10) PRIMARY KEY COMMENT 工号, name VARCHAR(20) NOT NULL, title VARCHAR(20) DEFAULT 讲师 COMMENT 职称, department VARCHAR(30) COMMENT 所属院系 ) ENGINEInnoDB COMMENT教师信息表; -- 课程表课程号为主键外键关联教师 CREATE TABLE course ( course_id VARCHAR(10) PRIMARY KEY COMMENT 课程号如CS101, course_name VARCHAR(50) NOT NULL COMMENT 课程名称, credit TINYINT UNSIGNED NOT NULL DEFAULT 2 COMMENT 学分, teacher_id VARCHAR(10) NOT NULL COMMENT 授课教师工号, FOREIGN KEY (teacher_id) REFERENCES teacher(teacher_id) ON UPDATE CASCADE ON DELETE RESTRICT ) ENGINEInnoDB COMMENT课程信息表; -- 选课表联合主键 双外键 级联删除 CREATE TABLE selection ( student_id VARCHAR(12) NOT NULL COMMENT 学生学号, course_id VARCHAR(10) NOT NULL COMMENT 课程号, grade TINYINT UNSIGNED COMMENT 成绩0-100可为空未录入, selected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 选课时间, PRIMARY KEY (student_id, course_id), FOREIGN KEY (student_id) REFERENCES student(student_id) ON UPDATE CASCADE ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES course(course_id) ON UPDATE CASCADE ON DELETE CASCADE ) ENGINEInnoDB COMMENT学生选课记录表;参数说明与设计理由VARCHAR(12)用于student_id预留扩展性如带年份前缀比INT更贴合真实学号规则且避免INT溢出风险ENUM(M,F)替代TINYINT语义清晰数据库层校验性别取值杜绝3或NULL异常ON DELETE RESTRICT在course表对teacher_id防止误删教师导致课程“孤儿化”必须先清空该教师所授课程selection表ON DELETE CASCADE学生退学时自动清除其所有选课记录无需 Java 层遍历删除 —— 这是关系型数据库的核心优势别浪费。3. Java 层分层实现为什么不用 Spring Boot因为你要亲手摸到 JDBC 的脉搏3.1 项目结构必须“裸”出来拒绝黑盒框架暴露每一层职责这是一个标准的三层架构不依赖任何框架目录结构如下使用 Maven 标准布局src/ ├── main/ │ ├── java/ │ │ └── com/example/selection/ │ │ ├── model/ // 实体类Student.java, Course.java, Selection.java │ │ ├── dao/ // 数据访问对象StudentDAO.java, CourseDAO.java, SelectionDAO.java │ │ ├── service/ // 业务逻辑SelectionService.java含事务控制 │ │ └── ui/ // 控制台交互MainApp.java无 Swing/Web纯 Scanner │ └── resources/ │ └── db.properties // 数据库连接配置 └── test/ └── java/... // JUnit 测试重点测 DAO 方法为什么坚持不用 Spring Boot课设答辩时老师问“事务在哪控制的” 你说“Transactional注解”他追问“注解背后怎么生效的”你就卡壳了而手写Connection.setAutoCommit(false)commit()/rollback()你能指着代码说“这里开启事务这里执行多条 SQL这里统一提交或回滚”所有 DAO 方法返回boolean或抛SQLException逼你处理SQLIntegrityConstraintViolationException如重复选课等具体异常而不是笼统的RuntimeException。3.2 核心 DAO 实现JDBC 模板的最小化封装以SelectionDAO.java为例这是选课功能的底层命脉public class SelectionDAO { private static final String INSERT_SQL INSERT INTO selection (student_id, course_id) VALUES (?, ?); private static final String DELETE_SQL DELETE FROM selection WHERE student_id ? AND course_id ?; private static final String SELECT_BY_STUDENT_SQL SELECT s.*, c.course_name, c.credit, t.name AS teacher_name FROM selection s JOIN course c ON s.course_id c.course_id JOIN teacher t ON c.teacher_id t.teacher_id WHERE s.student_id ?; public boolean insertSelection(String studentId, String courseId) throws SQLException { try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(INSERT_SQL)) { ps.setString(1, studentId); ps.setString(2, courseId); int rows ps.executeUpdate(); return rows 1; // 严格校验是否插入成功 } } public boolean deleteSelection(String studentId, String courseId) throws SQLException { try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(DELETE_SQL)) { ps.setString(1, studentId); ps.setString(2, courseId); int rows ps.executeUpdate(); return rows 1; } } public ListSelectionWithCourseInfo selectByStudent(String studentId) throws SQLException { ListSelectionWithCourseInfo list new ArrayList(); String sql SELECT_BY_STUDENT_SQL; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, studentId); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { SelectionWithCourseInfo item new SelectionWithCourseInfo(); item.setStudentId(rs.getString(student_id)); item.setCourseId(rs.getString(course_id)); item.setCourseName(rs.getString(course_name)); item.setCredit(rs.getByte(credit)); item.setTeacherName(rs.getString(teacher_name)); item.setSelectedAt(rs.getTimestamp(selected_at)); list.add(item); } } } return list; } }关键点解析DBUtil.getConnection()是自封装的连接获取方法内部用DriverManager 连接池HikariCP非new Connection()所有PreparedStatement参数用?占位杜绝 SQL 注入也避免字符串拼接的类型转换错误insertSelection()返回boolean而非void让上层业务能判断“是否真的插入了一行”而不是盲目认为成功selectByStudent()返回ListSelectionWithCourseInfo这是一个组合实体类非Selection包含课程名、学分、教师名等关联信息避免 N1 查询 —— 这是 JOIN 查询的正当用法不是偷懒。4. 避坑指南那些让课设答辩当场沉默的 4 个高频翻车点4.1 现象插入选课记录时报Duplicate entry 20230001-CS101 for key PRIMARY原因selection表主键是(student_id, course_id)联合主键但代码中未做前置校验直接执行INSERT。MySQL 拦截后抛出SQLIntegrityConstraintViolationException而 Java 层捕获后只打印堆栈未给用户友好提示。解决在SelectionService.insertSelection()中先调用selectionDAO.selectByStudentAndCourse(studentId, courseId)查询是否存在存在则返回“您已选过此课程”不存在再执行插入。永远不要依赖数据库异常做业务逻辑分支。4.2 现象删除学生时student表删除成功但selection表残留记录原因建表时selection.student_id外键未加ON DELETE CASCADE或 MySQL 版本低于 5.7 未启用外键检查SET FOREIGN_KEY_CHECKS 1。解决执行SHOW CREATE TABLE selection;确认外键定义含ON DELETE CASCADE若缺失用ALTER TABLE selection DROP FOREIGN KEY fk_student_id;删除旧外键再重建。外键不是建完就完事要验证行为。4.3 现象中文姓名/课程名存入数据库后变成???原因MySQL 服务端、数据库、表、列四级字符集不统一或 JDBC URL 缺少characterEncodingutf8mb4参数。解决检查 MySQL 配置文件my.cnf确保[client]和[mysqld]下均有default-character-set utf8mb4创建数据库时显式指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ciJDBC URL 必须为jdbc:mysql://localhost:3306/student_selection_system?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4。缺一不可少一个?后面的参数都不生效。4.4 现象SelectionService中调用studentDAO.update()和selectionDAO.insert()两个操作一个成功一个失败数据不一致原因未开启事务两个 DAO 方法各自用独立Connection无法保证原子性。解决在SelectionService中统一管理Connection传入各 DAO 方法public boolean selectCourse(String studentId, String courseId) throws SQLException { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交 boolean success studentDAO.checkExists(conn, studentId) courseDAO.checkExists(conn, courseId) selectionDAO.insertSelection(conn, studentId, courseId); if (success) { conn.commit(); } else { conn.rollback(); } return success; } finally { if (conn ! null !conn.isClosed()) conn.close(); } }注意DAO 方法签名需改为insertSelection(Connection conn, ...)将连接控制权交给 Service 层。5. 课设交付物 checklist答辩前必须亲手验证的 7 个硬指标一份能过关的课设不是“能跑就行”而是每个交付物都经得起放大镜审视。以下是某高校数据库课设评分细则提炼出的7 项必检项我带过的 A 同学曾因漏查第 5 项在答辩时被问“你这个成绩字段允许 NULL那未录入成绩和成绩为 0 怎么区分”当场哑火。序号检查项验证方法不合格表现示例1ER 图与表结构严格对应对照 ER 图中的联系基数、属性、主外键逐条核对建表 SQLER 图标student与course为多对多但建表缺selection表2所有外键均启用ON UPDATE CASCADE执行SHOW CREATE TABLE selection;确认外键定义含ON UPDATE CASCADE外键定义只有REFERENCES student(student_id)无级联动作3selection表主键为联合主键DESCRIBE selection;查看Key列PRIMARY应覆盖两列Key列显示PRI只在id字段自增假主键4成绩字段grade允许 NULLDESCRIBE selection;查看Null列grade行应为YESgrade行Null为NO导致未录入成绩时无法插入5成绩字段有合理取值约束尝试插入grade -1或grade 101应报错CHECK constraint failed插入成功说明缺CHECK (grade BETWEEN 0 AND 100)6Java 控制台输入支持中文在MainApp中输入中文学生姓名保存后查询显示正常控制台输出乱码或数据库存为??7事务测试模拟并发选课冲突开两个终端同时执行insertSelection(20230001, CS101)应仅一条成功两条都成功违反联合主键约束或都失败未捕获异常我的血泪经验每次交付前我会用一个文本文件记下这 7 条逐条打钩。尤其第 5 条成绩 CHECK 约束和第 7 条并发测试90% 的同学会忽略。第 7 条的并发测试不用写多线程代码就开两个命令行窗口手动敲两次相同选课命令看数据库最终几条记录 —— 这是最朴素也最有效的压力测试。最后提醒一句别把db.properties里的密码写成明文提交到 Git。用#password123456注释掉答辩时现场解注释。希望帮到你。本文还有配套的精品资源点击获取
延伸阅读

更多相关文章

2026/10/9 20:08:55

CTD数据库与MATLAB构建基因-药物-疾病关系图谱

1. CTD数据库不是“另一个生物数据库”,而是基因-药物-疾病关系的底层坐标系很多人第一次听说CTD(Comparative Toxicogenomics Database),下意识会把它和NCBI、Ensembl或KEGG归为一类——“又一个要查文献时顺手点开的网站”。这种…

2026/10/9 20:03:54

机内测量数据进MES:从测头变量到质量报表的完整链路

车间里最常见的那种场景,我想你多半见过:操作工手里攥着一张A4纸,站在机床门边等测头走完程序,然后麻利地抄几个数字;纸上沾了切削液,边角卷起来,字迹被油污浸得发灰。班组长下班收纸&#xff0…

2026/10/9 20:03:54

三菱PLC工单拼接:CONCAT与REPLACE选型及避坑指南

1. 工单拼接场景下字符串函数的选型逻辑工单拼接这件事,听起来简单,做起来全是细节。我在产线做数据采集和设备联调那几年,最常遇到的场景就是:PLC 要把产品型号、批次号、时间戳、工位编号拼成一条完整的工单字符串,然…

2026/10/9 22:34:46

从零手写小型编译器:词法分析、AST、字节码与虚拟机实现

简介:一份编译原理课程设计“实现一个小型编译程序”的完整资源包,适合计算机专业本科生或需要完成SLR(1)语法分析实验的开发者。项目基于C语言在Win10VS2019环境下开发,实现将高级语言源程序翻译为四元式程序(必做阶段&#xff0…

2026/10/9 22:34:46

VsCode+DeepSeek+Cline:AI编程助手初体验与TaoToken接入实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 22:34:46

数据库课设实战:进销存系统表结构设计与库存事务避坑指南

简介:这份数据库课程设计资源面向高校计算机及相关专业学生,围绕某商店进销存管理系统展开,适合正在完成数据库原理课程设计、需要参考完整案例的学习者。资源包共3个文件,包含1个bak数据库备份、1个sql脚本和1个doc课程设计报告&…

2026/10/9 22:29:46

鸿蒙开发实战:ArkTS仿网易新闻客户端完整实现与踩坑记录

做鸿蒙开发这段时间,我最大的感触是:ArkTS 这套声明式 UI 的乐趣和坑,都藏在完整项目里。这次我挑了一个特别经典的练手题材——仿网易新闻客户端,没有做复杂的账号体系,也没有堆服务端,就把“新闻列表页 …

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/9 20:15:56

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 0:04:27

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略当数万字的学位论文初稿经历开题、实验、问卷与多轮文献梳理最终成形时,绝大多数研究生都会面临一道全新的形式审查关卡:AIGC 疑似度排查。在高校毕业审核流程中,盲审前的文本检测通…

2026/10/9 0:04:27

食堂节能改造源头工厂,商用厨房设备焕新方案广受好评

商用厨房作为餐饮经营、单位供餐的核心后勤阵地,其设备配置、动线规划与运维体系直接决定后厨作业效率、运营成本与合规性。从基础的灶具、制冷存储设备,到油烟净化、水处理等配套系统,每一个环节的合理性都与食品安全、能耗管控、消防安全挂…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑