简介:这是一份基于Java Swing与MySQL实现的学生选课管理系统完整项目,面向Java初学者及课程设计、毕业设计人群,适合在IntelliJ IDEA或Eclipse中结合JDK8与MySQL5.7以上环境运行,帮助读者掌握桌面GUI开发与数据库增删改查的整合思路。系统包含系统管理、数据管理、数据查询三大模块:系统管理涵盖密码设置、用户登录与退出;数据管理支持学生、课程、选课信息的增加、删除和修改;数据查询则提供三类信息的检索功能。资源包共3个文件,压缩包大小仅3.34MB,内含zip工程源码、sql数据库脚本和docx实训报告,其中zip为可直接导入Idea/Eclipse的完整工程,sql为数据库初始化脚本,docx为课程设计报告,便于对照源码与文档进行二次开发。目前已有268人学习下载,通过该项目可清晰看到选课业务中的数据流转,快速搭建管理原型,同时参考实训报告完成课设文档。
1. 学生选课管理系统:一个能跑通、能讲清、能答辩的 Java Swing 课设样板
如果你正被「课程设计」三个字卡住,选课管理系统这套源码值得你完整过一遍。它不是一个只贴了界面截图的花架子,而是一个基于 Java + Swing + MySQL 的完整可运行工程:自带 studb.sql 数据库脚本、IDEA 可直接导入的项目结构,还附带一份课程设计报告文档。系统功能覆盖了课设答辩最常被问的三块——系统管理(登录、改密码、退出)、数据管理(学生、课程、选课的增删改)、数据查询(三类信息的多条件检索),恰好是「评委老师一看就知道你做了事」的经典闭环。
对新手来说,它最大的价值在于能让你在 IDEA 里几分钟跑起来,然后把 Swing 事件监听、JDBC 操作、SQL 建表这三件事串成一条完整链路;对想二次开发的人来说,它的模块划分足够清晰,加功能、换皮肤、扩展报表都有明确的下手位置。下面的内容以这份资源为基底,从环境搭建讲到核心代码,再讲到报告撰写和改造思路,照着做就能把这个项目变成「你的」。
2. 项目全局与运行环境:打开压缩包后你拿到的是什么
2.1 技术栈与兼容性:为什么选 Swing + MySQL 而不是别的
这个项目选型非常「课设标准答案」:界面层用 Swing,不依赖第三方 UI 框架,JDK 自带,双击就能跑;数据层用 MySQL 5.7 以上版本,配合 JDBC 驱动做直连,没有引入 MyBatis、Hibernate 这类重量级 ORM。这种组合的好处是能把「Java 基础语法 + 面向对象 + 数据库 SQL」这三大课设考核点全部覆盖到,同时又不会因为框架版本问题在答辩现场翻车。
运行环境要求很宽松:IDEA 或 Eclipse 都行,JDK 8 即可,MySQL 5.7 以上。如果你本地装的是 MySQL 8.x 也完全没问题,只需要注意驱动包版本和连接 URL 的时区参数,第四章会专门讲这个坑。整个工程是纯 JDBC + Swing,没有 Maven 依赖管理,用的是项目里自带的 lib 目录放 jar 包,这对不熟悉 Maven 的新手反而是好事——不用等拉取依赖,离线也能跑。
2.2 压缩包内容与文件职责:哪个文件是干嘛的
解压后你会看到四个核心部分,先花两分钟认清它们,避免后面手忙脚乱:
| 文件/目录 | 类型 | 作用 |
|---|---|---|
studb.sql | SQL 脚本 | 建库建表语句,含初始数据,必须最先导入 MySQL |
studentCourseSelect-idea.zip | 工程压缩包 | 里面是 IDEA 可直接打开的完整 Java 工程 |
| 课程设计报告.docx | 文档 | 约 30 页左右的课设报告,含需求分析、设计、测试、总结 |
lib目录(在工程内) | 依赖库 | mysql-connector-java 驱动 jar 包 |
这里有个细节要注意:数据库文件名是studb.sql,也就是说默认数据库名是studb,不是student也不是course_select。如果你自己改了库名,记得同步改 Java 代码里的 JDBC URL,否则一启动就连不上数据库。我一般会先打开这个 SQL 文件看一眼建表语句,确认表名字段名和代码里 SQL 语句完全一致,再往下走。
2.3 数据库初始化步骤:导入 studb.sql 的两种方式
方式一:命令行导入(推荐新手用,能看清执行过程)
mysql -u root -p < studb.sql执行后输入 MySQL 的 root 密码即可。导入完成后用mysql -u root -p进入客户端,执行SHOW TABLES;查看,正常情况下能看到三张主表加一张用户表。这个命令的含义是:用 root 身份连接本地 MySQL,把 studb.sql 里的所有 SQL 语句按顺序执行。如果你的 MySQL 没把 bin 目录加入系统 PATH,需要写全路径,或者直接在 MySQL 安装目录下执行。
方式二:Navicat / MySQL Workbench 图形化导入
打开连接后右键「运行 SQL 文件」,选中 studb.sql 执行。注意执行前先确认目标库是空的,避免表名冲突。图形化工具的好处是能直接看到每张表的行数,方便验证数据是否导入成功。导入完成后建议立刻把三张表的数据浏览一遍,课设报告里的测试截图也建议从现在开始攒素材。
2.4 IDEA 导入工程与启动:避免 ClassNotFound 的注意点
IDEA 导入时选「Open」定位到 studentCourseSelect 工程目录,不是直接打开 zip。导入后如果看到一堆红色报错,九成是 JDK 没选对:File → Project Structure → SDK 选 JDK 8。然后把lib目录下的 mysql-connector-java.jar 添加为 Library:右键 lib 目录 → Add as Library,这一步漏了运行时会抛ClassNotFoundException: com.mysql.jdbc.Driver。
启动入口在某个包含main方法的类里,通常是登录窗口类。直接右键运行即可,如果报数据库连接失败,回到数据库配置类里核对 URL、用户名、密码三项。工程里默认用户是 root 密码为空或 123456,各自身边环境不一样,这个必须改成你自己的。建议第一次启动前先把这三处配置截图存一份,答辩时老师问「数据库连接怎么配的」可以直接展示。
3. 核心模块拆解:一个登录请求从点击到跳转的完整链路
3.1 登录验证:JDBC 查询与 Swing 事件监听的第一次握手
整个系统的入口是用户登录,也是课设答辩时老师几乎必问的模块。登录界面的核心逻辑分两步:用户在文本框输入用户名密码,点击登录按钮触发actionPerformed事件,然后通过 JDBC 拼 SQL 去user表里查记录。
// LoginFrame.java 登录按钮核心代码 private void loginButtonActionPerformed(ActionEvent e) { String username = usernameField.getText().trim(); String password = new String(passwordField.getPassword()); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, "用户名和密码不能为空"); return; } // 通过 DBUtil 工具类获取数据库连接 Connection conn = DBUtil.getConnection(); String sql = "SELECT * FROM user WHERE username = ? AND password = ?"; try { PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); ResultSet rs = ps.executeQuery(); if (rs.next()) { // 登录成功,关闭登录窗口,打开主界面 new MainFrame().setVisible(true); this.dispose(); } else { JOptionPane.showMessageDialog(this, "用户名或密码错误"); } rs.close(); ps.close(); conn.close(); } catch (SQLException ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, "数据库连接异常"); } }这段代码里有几个值得在答辩时讲清楚的细节。第一,密码框用的是getPassword()而不是getText(),前者返回 char 数组,防止密码以 String 形式常驻内存,这是 Swing 开发里容易忽略的安全习惯。第二,SQL 用PreparedStatement做参数化查询,而不是字符串拼接,这么做能防止 SQL 注入——你把' OR '1'='1拼进去试试,参数化查询会把它当普通字符串处理。第三,rs.next()是判断结果集是否有数据的关键方法,游标初始位置在第一条记录之前,第一次调用next()才会指向第一条记录。
3.2 主界面框架:选项卡面板如何组织三大功能模块
登录成功后进入主界面,主界面用JTabbedPane做功能分区,每个页签对应一个功能域。这种设计的好处是模块边界清晰,后续加功能只需要新增一个页签,不会动到已有代码。主界面通常包含「学生管理」「课程管理」「选课管理」「查询统计」等页签,每个页签内部放一个JPanel,面板上半部分是查询条件区,中间是JTable表格展示区,底部是操作按钮区,这是 Swing 管理类界面的经典三段式布局。
// MainFrame.java 主界面页签构建核心代码 public MainFrame() { initComponents(); // 创建各个功能面板,加入选项卡 JTabbedPane tabbedPane = new JTabbedPane(); StudentPanel studentPanel = new StudentPanel(); // 学生管理页签 CoursePanel coursePanel = new CoursePanel(); // 课程管理页签 SelectPanel selectPanel = new SelectPanel(); // 选课管理页签 QueryPanel queryPanel = new QueryPanel(); // 综合查询页签 tabbedPane.addTab("学生管理", studentPanel); tabbedPane.addTab("课程管理", coursePanel); tabbedPane.addTab("选课管理", selectPanel); tabbedPane.addTab("数据查询", queryPanel); this.add(tabbedPane, BorderLayout.CENTER); }从代码可以看到,每个管理模块被封装成一个独立的JPanel子类,各自维护自己的表格数据模型和增删改查方法。tabbedPane.addTab("学生管理", studentPanel)这行代码理解起来就是一个「页签名 + 面板实例」的绑定关系。如果你想加一个「教师管理」模块,只需要新建一个TeacherPanel,然后照葫芦画瓢 addTab 进来,这就是整个项目扩展性最好的位置。
3.3 学生管理增删改:表格选中行与数据模型的联动
学生管理页签的核心操作是:表格展示学生列表 → 选中一行 → 点修改按钮 → 表单回填 → 保存到数据库。这里最关键的技巧是把 JTable 的选中行和数据库记录一一对应起来,一般通过行号row去取表格模型TableModel里的主键值。
// StudentPanel.java 修改学生信息核心代码 private void updateButtonActionPerformed(ActionEvent e) { int row = studentTable.getSelectedRow(); if (row == -1) { JOptionPane.showMessageDialog(this, "请先选择要修改的学生"); return; } // 从表格模型中取出该行的学生学号(主键) String sno = (String) studentTable.getModel().getValueAt(row, 0); String sname = nameField.getText().trim(); String ssex = sexCombo.getSelectedItem().toString(); int sage = Integer.parseInt(ageField.getText().trim()); String sql = "UPDATE student SET sname = ?, ssex = ?, sage = ? WHERE sno = ?"; try { Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, sname); ps.setString(2, ssex); ps.setInt(3, sage); ps.setString(4, sno); int result = ps.executeUpdate(); if (result > 0) { JOptionPane.showMessageDialog(this, "修改成功"); refreshTable(); // 刷新表格数据 } ps.close(); conn.close(); } catch (SQLException ex) { ex.printStackTrace(); } }这里有个「表格选中行 ≠ 数据库第一行」的细节:getSelectedRow()返回的是视图行号(显示给用户看的表格行号),不是数据库记录的绝对位置。所以代码里先通过视图行号去 Model 里取学号这个主键值,再用主键去 update,这样无论是按姓名排序还是筛选后的结果,更新时都不会错位。这也是课设答辩时老师喜欢追问的一个点——如果你答不上来,至少能说出「通过主键定位记录」这个思路。
executeUpdate()的返回值表示受影响的行数,大于 0 说明更新成功。这是 JDBC 里判断增删改是否生效的标准套路。删除操作的逻辑更简单,拿到选中行的主键后执行DELETE FROM student WHERE sno = ?即可,但要不要做二次确认弹窗,是这个模块常见的加分项,建议加上。
3.4 数据查询:多条件拼接与 ResultSet 到 TableModel 的转换
数据查询模块是整个系统里最有技术含量的一块,它要解决的是「多条件组合查询」——用户可能只输入姓名,也可能同时输入姓名和院系,SQL 语句必须动态拼接。这门课设里常见的做法是先拼 WHERE 子句,再遍历条件判断是否追加。
// QueryPanel.java 条件查询核心代码 private void queryButtonActionPerformed(ActionEvent e) { String sname = nameField.getText().trim(); String sdept = deptField.getText().trim(); String ssex = sexCombo.getSelectedItem().toString(); // 动态拼接 WHERE 条件 StringBuilder sql = new StringBuilder("SELECT * FROM student WHERE 1 = 1"); List<Object> params = new ArrayList<>(); if (!sname.isEmpty()) { sql.append(" AND sname LIKE ?"); params.add("%" + sname + "%"); } if (!sdept.isEmpty()) { sql.append(" AND sdept = ?"); params.add(sdept); } if (!"全部".equals(ssex)) { sql.append(" AND ssex = ?"); params.add(ssex); } // 执行查询并填充表格 try { Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql.toString()); for (int i = 0; i < params.size(); i++) { ps.setObject(i + 1, params.get(i)); } ResultSet rs = ps.executeQuery(); fillTable(rs); // 将结果集填充到 JTable rs.close(); ps.close(); conn.close(); } catch (SQLException ex) { ex.printStackTrace(); } }这段代码最值得学习的是WHERE 1 = 1这个写法。它的作用不是查询所有记录,而是给动态拼 SQL 提供锚点——后续每个条件都是AND xxx = ?,永远不用判断「这是不是第一个条件」。StringBuilder用来拼接可变字符串,比直接String + String性能更好,这也是答辩时能随口说出来的技术点。List<Object> params配合ps.setObject(i + 1, ...)是参数化查询的进阶用法,能统一处理 String、Integer 等不同类型参数的传值。
3.5 选课管理:绑定学生与课程的多表关联操作
选课管理模块涉及学生表和课程表的关联关系,核心逻辑是:界面上提供学生下拉框和课程下拉框,选完后点击选课按钮,往选课表里插入一条记录。选课表通过外键关联学生和课程,插入前要检查是否重复选课,否则会出现同一个人选同一门课两次的数据脏问题。
这个模块的代码套路和数据管理类似,但有一点关键差异:下拉框里的选项要「显示姓名、存下学号」,Swing 里的做法是用自定义对象或Map做数据模型。开发中常用的做法是定义一个小实体类StudentItem,内部持有sno和sname两个字段,重写toString()返回姓名,这样JComboBox显示的是姓名,getSelectedItem()拿出来后能直接取到学号。这是 Swing 开发里非常实用的小技巧,课程设计报告里也可以写上这一笔。
4. 避坑指南:从导入到答辩的常见问题与排查方案
4.1 现象一:启动即报ClassNotFoundException: com.mysql.jdbc.Driver
原因:lib 目录下的 mysql 驱动 jar 包没有被 IDEA 识别为项目依赖。很多人从压缩包里解压出工程后,lib 目录虽然存在,但 IDEA 不会自动把里面的 jar 加入 classpath。
解决:打开工程后右键lib目录 →Add as Library,IDEA 会弹窗让你确认 Level,选Project Library即可。Eclipse 用户则对应Build Path→Add External Jars。验证是否成功的方式是看 Project Structure 里的 Libraries 面板是否出现该 jar 包。
4.2 现象二:数据库连不上,报Communications link failure
原因:这通常是 MySQL 服务没启动,或者连接 URL 里的端口/时区参数不匹配。MySQL 8.x 对时区参数要求更严格,连接 URL 里必须有serverTimezone,否则会直接拒绝连接。
解决:先在命令行执行net start mysql(Windows)或systemctl start mysqld(Linux)确认服务在跑。然后检查 JDBC URL 是否形如jdbc:mysql://localhost:3306/studb?useSSL=false&serverTimezone=Asia/Shanghai。我一般会先写死useSSL=false&serverTimezone=Asia/Shanghai这两段,避免不必要的握手开销和时区报错。如果你用的是 MySQL 5.7,这两段参数可加可不加,但加了也不会出错。
4.3 现象三:查询结果中文乱码,表格里显示问号
原因:数据库表编码和 JDBC 连接编码不一致。很多课设的 SQL 脚本建表时用的是utf8,但代码里没设置连接字符集,导致浏览器层面已经是 UTF-8 的字符串通过网络传输到 MySQL 时变成乱码存储。
解决:在 JDBC URL 后面追加characterEncoding=utf8,完整写法是jdbc:mysql://localhost:3306/studb?useSSL=false&characterEncoding=utf8。同时确认studb.sql文件本身是以 UTF-8 编码保存的(用 Notepad++ 或 IDEA 右下角可看)。改完这两处重启程序,乱码问题基本消失。如果是 Windows 下 IDEA 控制台乱码,还可以加 JVM 参数-Dfile.encoding=utf-8。
4.4 现象四:能登录但点增删改没反应,控制台异常也不打印
原因:多半是数据库账号权限不够,或者是主键约束冲突。比如你往学生表插入一条学号重复的记录,MySQL 会抛出Duplicate entry异常,但有些课设代码里的异常处理直接printStackTrace(),控制台如果被吞了输出就看不到。
解决:在 catch 块里临时改成JOptionPane.showMessageDialog(this, ex.getMessage()),让异常直接在界面上弹出来。这是排查 JDBC 问题的有效手段——先在界面上看到真实错误信息,再对症处理。改完排查后记得改回printStackTrace(),否则答辩演示时弹出一堆异常信息不太好看。插入失败的重要原因还有自增主键没设置、外键约束失败,这些都能从ex.getMessage()里读到准确原因。
4.5 现象五:IDEA 里能跑,但打包成 jar 后双击没反应
原因:Swing 程序的入口类没有写main方法跳转,或者 MANIFEST 里没指定 Main-Class。很多课设源码习惯直接运行单窗口类,而正式交付时需要可执行 jar。
解决:确认工程里有一个类包含public static void main(String[] args),用它作为启动类。打包时用 IDEA 的 Artifacts 功能,Main Class 选该启动类。注意 lib 下的驱动 jar 如果要打进可执行包,需要选择Copy to the output directory and link via manifest选项,否则换台机器跑会找不到驱动。
5. 实训报告:让文档成为答辩时的第二张王牌
5.1 报告的定位:不是凑字数,是给答辩老师划重点
课程设计报告这份 docx 的价值被很多人低估了——它不只是一份要被老师翻看和批注的文字材料,更是你答辩时手里拿的讲解地图。这份报告本身结构已经比较完整,通常包含需求分析、总体设计、数据库设计、详细设计、系统测试和总结几大块。你要做的不是从头重写,而是「对号入座」:把第三章里那几个核心代码片段换成你自己加深过的写法,把数据库表结构截图换成你自己导入后的实际数据截图。
特别要说的是测试部分。很多课设报告写测试就是贴几个界面截图,但如果能在测试里加上「输入异常数据(空值、超长字符串)→ 系统给出友好提示」这类有逻辑的记录,答辩时老师的提问重心立刻会从「这功能是不是你自己写的」转向「你是怎么考虑异常情况的」,这属于典型的主动引导提问方向。
5.2 从代码到文档:保持代码片段、表结构、功能清单的一致性
报告里最容易扣分的是前后矛盾:需求分析里写「系统支持按院系查询」,但详细设计的截图里根本没这个功能;数据库设计里student表有 5 个字段,但代码里 insert 语句只写了 4 个。建议花一个小时做一次「三对照」检查:把报告里提到的每个功能点、每张数据表、每个界面对应起来,用表格列出来,缺哪补哪。
另外报告里所有涉及运行的截图建议统一为同一个分辨率风格,不要一会全屏一会半屏。我在做课设时吃过这个亏,老师翻报告时看到截图下沿的电脑任务栏和聊天窗口,观感直接打折。现在养成习惯:截图前先关闭无关应用,把窗口固定在居中位置再截。
5.3 让文档具备「可升级」属性:预留扩展点
再看一遍报告的结构,你会发现「系统设计」这章其实是最有发挥空间的。比如数据库设计部分,可以补一张 E-R 图,说明学生和课程之间是多对多关系,通过选课表实现关联——这是选课系统的核心数据模型,画出来能让报告的专业度上一个台阶。再比如系统架构部分,可以注明「本系统采用 MVC 思想的变体:Swing 界面对应 View,业务逻辑放在各 Panel 类中,DBUtil 对应 Model 层的数据访问」,答辩时老师问「三层架构了解吗」,你能用自己的项目对应讲一遍,印象分会好很多。
6. 二次开发思路:给这个课设项目做三个会被加分的改进
6.1 加一个带角色的登录权限控制
现在系统的登录验证只查用户名密码是否匹配,不管用户身份。你可以在 user 表里加一个 role 字段('admin' 或 'student'),登录成功后把角色存到当前会话对象里,再在MainFrame里根据角色决定哪些页签可见或按钮可用。
// 登录成功后按角色控制主界面功能权限 if ("admin".equals(role)) { tabbedPane.addTab("数据管理", managePanel); } else { // 学生角色只能看到查询页签和自己的选课情况 tabbedPane.removeTabAt(tabbedPane.indexOfComponent(managePanel)); }这个改动的价值在于让系统从「单角色工具」变成「多角色系统」,是课设答辩里一个亮眼的加分项,而且实现难度不大——加一个字段、加一个判断、控制页签显隐即可。角色枚举和会话状态的管理还能顺带讲清楚状态设计的思路。
6.2 把表格数据导出成 Excel
课设系统里数据量不大,但「导出 Excel」这个功能非常实用,尤其是数据查询模块里查出来的结果,老师会希望能在 Excel 里继续处理。Swing 里做导出常见做法是生成 CSV 文件,因为 CSV 可以用 Excel 直接打开,不需要引入 POI 等重型依赖。
// 导出查询结果为 CSV 文件 FileWriter writer = new FileWriter("student_list.csv"); for (int i = 0; i < table.getRowCount(); i++) { StringBuilder row = new StringBuilder(); for (int j = 0; j < table.getColumnCount(); j++) { if (j > 0) row.append(","); row.append(table.getValueAt(i, j)); } writer.write(row.toString() + "\n"); } writer.close();导出时的关键点是处理字段内含逗号或换行的情况,否则 CSV 会错列。可以加一个简单的转义方法:字段里如果包含逗号、引号或换行,就用双引号包起来。这个功能只用一个按钮就能加完,非常划算。
6.3 把密码加密存储
现在的 user 表里密码是明文,一查数据库就能看到,这在课设层面问题不大,但如果你想把这些改进写成亮点,密码 MD5 加盐是成本最低的安全升级。在登录验证时不再直接password = ?,而是先MD5(username + password)再比对。
-- 建表时把 password 字段设置为固定长度字符串 ALTER TABLE user MODIFY password VARCHAR(64) NOT NULL;要注意的是改造后需要把所有已有账号的密码同步更新成加密后的值,否则登录全部失败。写个临时脚本跑一遍即可,这也算是一次完整的数据迁移演练。安全这件事在课设里不用做得太重,但能让评审老师看到「这个学生考虑了密码不能明文存储」就是赚到。
6.4 验证与收尾:改完好不好,用三个维度验证
任何改动完成后,我会强制自己走一遍「回归三件套」:第一,把删除和修改操作目标从「当前表格选中行」改成「条件查询后的选中行」,确认在筛选状态下增删改依然正确;第二,打开数据库客户端直接检查数据,确认程序界面的操作真的落库了而不是只改了内存里的 TableModel;第三,断网或停掉 MySQL 服务,确认程序弹出的是友好错误提示而不是满屏堆栈。这套流程我从做第一个课设项目开始就养成了习惯,现在接手新需求连测试用例都不用写太多,因为每次改完代码都把这三步当默认动作。希望这套拆解能帮到你,少走几步弯路。
本文还有配套的精品资源,点击获取