简介这是一套基于Java Swing MySQL的学生选课及成绩管理系统课程设计适合Java初学者或希望掌握桌面应用开发的学习者可作为毕业设计、课程设计、大作业或工程实训的完整参考。项目覆盖登录、学生信息管理、课程管理、选课及成绩管理等核心功能内置管理员、教师、学生三类测试账号数据库配置和SQL脚本均随包提供便于快速运行与二次开发。资源共53个文件以Java源码、JPG界面截图、XML配置和SQL脚本为主另含PNG数据库E-R图与README说明整体压缩包仅8.63MB。截图覆盖登录、选课、成绩管理等主要界面可直观展示系统功能与交互流程。目前已有219人学习使用适合用来理解Swing界面编程、JDBC数据库操作以及分层项目组织方式也可作为课程答辩或项目文档的实用素材。1. 基于 Java Swing MySQL 的学生选课及成绩管理系统课设表象下的完整 CRUD 样本这套系统的压缩包解压后第一眼看上去就是标准的课设模板src 目录、lib 下躺着 mysql-connector-j-8.3.0.jar、document 里塞满了界面截图和 E-R 图。但等我把 SQL 脚本导入 MySQL 8、在 IDEA 里跑通登录后发现它比很多挂着“系统”名头的付费项目扎实得多——管理员、教师、学生三种角色权限分离选课和成绩录入用的是同一套 JDBC 逻辑甚至数据库配置类都单独抽了出来。对正在找 Java 课程设计案例源码的人来说这套项目最值钱的地方不是功能多炫而是能让你完整看到 Swing 表单和 MySQL 表之间是怎么通过 JDBC 串起来的。想啃下 Java 基础、需要交数据库课设、或者想快速搭一个桌面端 CRUD 原型做毕设起点的人都值得在电脑里留一份。2. 从源码到跑通环境准备、项目导入与数据库初始化2.1 先把 JDK、IDEA、MySQL 对齐别让版本拖后腿课程设计最常见的问题不是代码写错而是环境不对。这套系统的 README 里明确写了开发工具组合JDK 1.8、IDEA、MySQL 8.0.36、Navicat 16。从工具版本能看出项目作者是在一个非常典型的 Windows 课设环境下完成调试的这也意味着你用同样的组合去跑踩坑概率最低。JDK 1.8 对 Swing 支持最稳定不需要额外引入 JavaFXMySQL 8.0.36 对应 mysql-connector-j-8.3.0 驱动两者兼容性没问题。如果你电脑里装的是 MySQL 5.7驱动也能用但部分 SQL 语法和字符集默认值会有细微差别后面我会在避坑章节细说。建议先自查三个工具的版本再继续往下走。打开命令行java -version mysql --version# 如果提示 mysql 不是内部或外部命令说明 MySQL 没装进 PATH # 要么重新安装并勾选 Add to PATH要么用安装目录下的 bin/mysql.exe 代替JDK 输出到 1.8.0_xx 就没问题。MySQL 输出 8.0.xx 最好。如果没装 MySQL不要凭感觉从网上下最新版按 mysql 安装教程走安装时记住 root 密码。Navicat 16 不是必需的但用它执行 SQL 脚本和看表结构最直观我用的是这个工具后面导入 SQL 脚本的步骤也以它为例。至于 IDEA社区版就够课程设计不需要付费版特性。确认完环境后打开 IDEA选择“File New Project from Existing Sources”然后定位到解压后的 StuSystem 文件夹选择 Maven 或普通项目都可以——不过这套源码大概率不是 Maven 结构所以选“Import project from external model”时如果它提示找不到 pom.xml就直接选“Next”按普通 Java 项目导入。导入后 IDEA 会自动识别 src 目录但 lib 下的 mysql-connector-j-8.3.0.jar 需要你手动添加依赖右键 lib 目录选择“Add as Library”确认把 jar 包挂到项目模块上。这一步漏了的话编译期不会报错运行一调用 Class.forName 就抛 ClassNotFoundException。2.2 解压 StuSystem.zip看清目录再动手项目压缩包内部目录结构非常规整即使没有 README光看文件夹名也能猜到一半功能。下面是解压后主要内容的说明文件或目录用途src/comJava 源码包包含界面类、业务逻辑类、数据库配置类lib/mysql-connector-j-8.3.0.jarMySQL JDBC 驱动8.3.0 版本src/resources/sql数据库初始化脚本导入 MySQL 后得到所有表结构和初始数据document系统结构图、E-R 图、各界面截图写课设报告时直接能用README.md项目说明和默认账号密码src 目录下还有 .idea 文件夹说明作者用的是 IDEA 保存的工程配置这也可以作为该资源是“IDEA 原生开发”的佐证。但我不建议直接用他的 .idea因为每个人的本地 JDK 路径和配置不同容易把模块加载错误带过来。我一般会手动新建一个普通 Java 项目再把 src 复制进去然后手动导入 lib。这里有一个值得留意的点源码在 src/com 下分层常见包名是 com.ui登录页面和主窗体、com.service业务逻辑、com.dao数据访问、com.model实体类。从项目正文提到的 JdbcConfig 来看数据库连接配置被放在了 com.service 包下说明作者把“获取数据库连接”当作服务来处理这比把连接 URL 散落在每个 DAO 类里要好。你可以打开 src/com/service/JdbcConfig 看一下代码里大概率写死了一组常量package com.service; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class JdbcConfig { // 本地数据库地址3306 是 MySQL 默认端口 private static final String URL jdbc:mysql://localhost:3306/stusystem?useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 123456; static { try { // 注册驱动8.x 使用 com.mysql.cj.jdbc.Driver Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }这段代码的逻辑很直白先加载驱动类再通过 DriverManager 拿连接。URL 里的 useSSLfalse 是 MySQL 8 驱动连接本地库时的常用配置如果你看到项目里没有加这个参数后面排查 SSL 错误时可以补上。USER 默认 rootPASSWORD 默认 123456和 README 里说的一致。如果你本地 MySQL 密码不是 123456改这一处就够了但前提是你要先完成下一步的数据库初始化不然改了密码也连不上空库。2.3 初始化数据库执行 SQL 脚本改对连接参数数据库脚本放在 src/resources/sql 下文件名以解压后的实际 .sql 文件为准常见的是 init.sql 或 stusystem.sql。拿到脚本后不要急着双击运行先确认你要创建的库名和连接 URL 保持一致。以 JdbcConfig 里 URL 为 stusystem 为例你可以用 Navicat 执行也可以直接用命令行mysql -u root -p src/resources/sql/stusystem.sql# 如果提示 mysql 命令不存在cd 到 MySQL 安装目录的 bin 下再执行 # 比如cd C:\Program Files\MySQL\MySQL Server 8.0\bin # mysql -u root -p D:\StuSystem\src\resources\sql\stusystem.sql执行过程中会输入 root 密码。脚本里应该包含 CREATE DATABASE、CREATE TABLE 和 INSERT 初始数据的语句。如果脚本本身没有 CREATE DATABASE你需要先在 Navicat 里手动创建一个名为 stusystem 的数据库字符集选 utf8mb4再运行脚本。否则后面 JdbcConfig 连接时会报“Unknown database”。脚本导入成功后在 Navicat 里刷新应该能看到 student、teacher、course、sc选课表等表。注意初始数据的登录账号是硬编码在脚本里的管理员 admin/123教师 tch/123学生 stu/123。你可以打开对应的表确认密码字段是否是明文是明文的话说明这属于教学项目惯例方便演示同时也意味着如果要投产用密码加密是必改项。接下来启动系统。在 IDEA 里找包含 main 方法的入口类通常是 com.ui.LoginFrame 或 MainFrame右键运行。如果弹出一个 Swing 登录窗口说明项目已经跑通。如果报数据库连接失败优先回头看 JdbcConfig 里的密码是否改到位以及脚本是否成功创建了库。这两个地方是新手最容易卡住的点。3. 数据库表设计和权限模型三类角色是怎么串起来的3.1 从 E-R 图反推表结构学生、教师、课程、选课、成绩的关系document 目录下的“数据库E-R图.png”给出了整个系统的核心关系。从命名上看表设计遵循经典的教务管理模型学生和课程之间是多对多关系通过选课表sc作为中间表而成绩就是选课表上的一个附加属性。教师和课程之间则是一对多关系一个教师可以教多门课。这个模型在数据库课程设计中属于标准答案没有花哨设计但足够支撑选课和成绩管理的全部业务。常见的表结构是这样的表名作用关键字段建议student学生信息sno学号、sname、ssex、sdept、passwordteacher教师信息tno工号、tname、tdept、passwordcourse课程信息cno课程号、cname、credit、tno授课教师sc选课表sno、cno、score成绩admin管理员信息账号、密码从界面截图也能印证这个设计学生信息管理界面里必然有学号和姓名列学生选课界面里有课程列表和选课按钮学生课程成绩管理界面里有成绩列。E-R 图里这三张核心表的关系可以转化为一条联合查询 SQL这也是成绩查询界面的底层逻辑SELECT s.sname AS 学生姓名, c.cname AS 课程名称, sc.score AS 成绩 FROM student s JOIN sc ON s.sno sc.sno JOIN course c ON sc.cno c.cno WHERE s.sno stu;-- 上面的 WHERE 条件在真实代码里会用 PreparedStatement 的 ? 占位符替换 -- 查询效果把学生选的所有课和对应成绩一次性查出来这条 SQL 是理解整个系统业务的关键也是你在答辩时最容易被追问的语句。值得注意的是 sc 表中的 score 字段可空——学生刚选课还没考试时这条选课记录存在但成绩是 NULL。成绩录入功能本质上就是对 NULL 值做 UPDATE而不是 INSERT。3.2 三类角色的权限边界管理员管人、教师管课、学生选课如果把系统看成一个黑匣子登录框的下拉选角色就是黑匣子的第一道开关。项目里有三种角色它们的数据操作边界完全不同角色能操作的功能数据影响范围管理员学生信息管理、教师信息管理、课程管理、系统报表可以增删改所有用户和课程教师成绩录入、成绩修改、查看所授课程名单只能操作自己负责的课程和学生成绩学生选课、退选、查询成绩、修改密码只能操作自己的选课记录和成绩从登录后进入的主界面截图能明显看出不同角色的主窗口底部导航按钮不一样。这个设计不是靠多个独立程序实现的而是同一个主窗体类根据传入的 role 参数动态决定显示哪些面板。这里抛出一个实现思路也是你读源码时值得注意的地方// 登录成功后的角色判断逻辑 String role (String) roleBox.getSelectedItem(); if (admin.equals(role)) { mainFrame.showAdminPanel(); // 显示学生、课程、教师管理面板 } else if (teacher.equals(role)) { mainFrame.showTeacherPanel(); // 显示成绩录入和课程名单面板 } else { mainFrame.showStudentPanel(); // 显示选课和个人成绩面板 }思路是先把每种角色对应的面板做成单独方法然后按角色开关。这个写法的好处是后续新增角色时只需要在登录校验里加一个分支再实现对应的 showXxxPanel。坏处是如果角色增多mainFrame 类会膨胀。但对于课设规模这个方案是性价比最高的。3.3 为什么选 MySQL 8 JDBC 而不是 MyBatis课设场景的现实取舍很多同学看到这是 Java 项目第一反应是“为什么不用 MyBatis”。答案是Swing 是桌面程序没有 Web 容器引入 MyBatis 需要额外的 mybatis-config.xml 和 mapper 文件配置复杂度反而高于直接写 JDBC。项目正文里明确写了数据库连接驱动是 mysql-connector-j-8.3.0.jar这是纯 JDBC 方案。课设答辩时老师关注的重点是你的 SQL 有没有写对、事务有没有处理、连接有没有关闭而不是框架用得多少。JDBC 的代码逻辑直白每一行都能对着课本找到出处反而更容易解释。另外你可能会在搜资料时看到大量“java面试题”里问 JDBC 连接池、PreparedStatement 预编译这些概念这套系统正好能作为理解这些问题的实体案例。如果你后续想要提升可以把它改造成接入 Druid 连接池这在第 6 章我会给出具体改法和面试价值。从数据库选型角度MySQL 8 是目前课设报告最容易写“为什么选它”的答案开源免费、跨平台、支持 SQL 标准、可通过 Navicat 可视化操作。如果你在 Linux 服务器上部署mysql 安装和连接配置的思路也完全一样只是安装命令不同。这套项目的核心逻辑不依赖 Windows 特性所以跨平台迁移成本低。4. Swing 界面与业务逻辑选课、成绩录入和查询是怎么落地的附关键代码4.1 登录与新窗口切换JFrame 和事件监听的最小骨架任何 Swing 应用的第一步都是 JFrame。这套系统的登录界面截图显示它包含账号输入框、密码输入框、角色下拉框和登录按钮。登录按钮的点击事件是整个系统的入口它的实现模式也是 Swing 事件监听的标准写法loginBtn.addActionListener(new ActionListener() { Override public void actionPerformed(ActionEvent e) { // 1. 从输入框取值并拼接角色 String username usernameField.getText(); String password new String(passwordField.getPassword()); String role roleBox.getSelectedItem().toString(); // 2. 调用 service 层校验 boolean ok loginService.checkLogin(username, password, role); // 3. 根据校验结果跳转或提示 if (ok) { JOptionPane.showMessageDialog(LoginFrame.this, 登录成功); new MainFrame(role, username).setVisible(true); LoginFrame.this.dispose(); // 隐藏登录窗口 } else { JOptionPane.showMessageDialog(LoginFrame.this, 账号或密码错误请重试); } } });逻辑说明第一步从 JTextField 拿字符串从 JPasswordField 拿密码数组。这里注意密码框不能用 getText()因为返回的是 String 会留在内存里用 getPassword() 拿 char[] 用完即可清空更安全。校验方法 loginService.checkLogin 内部会执行 SQL 查询拿用户输入的账号密码和角色去对应表里比对。登录成功后把当前角色和用户名传给 MainFrame再 dispose 掉登录窗口避免关主窗体后登录页面还残留在后台。参数说明role 变量决定用户能看哪些功能username 决定后续查询以谁的身份进行比如学生查询成绩时要用 username 作为学号条件。这里有一个小细节如果用户名和角色不匹配checkLogin 必须返回失败否则会出现“学生账号选了管理员角色登录成功”的问题。4.2 学生选课把课程列表读进 JTable再写入选课表学生选课界面的核心流程是进入界面后查询所有可选课程展示在 JTable 中学生选中一行点击选课按钮系统往 sc 表插入一条记录。这里最关键的是“可选课程”的 SQL 怎么写一个合格的做法是使用 NOT IN 子查询排除已选课程// 查询当前学生还没选的课程 String sql SELECT cno, cname, credit FROM course WHERE cno NOT IN (SELECT cno FROM sc WHERE sno ?); PreparedStatement ps conn.prepareStatement(sql); ps.setString(1, sno); // sno 是当前登录学生的学号 ResultSet rs ps.executeQuery(); // 把结果集填充到 JTable 的 DefaultTableModel 里 DefaultTableModel model (DefaultTableModel) table.getModel(); model.setRowCount(0); // 清空上一次残留的数据 while (rs.next()) { model.addRow(new Object[] { rs.getString(cno), rs.getString(cname), rs.getDouble(credit) }); }逻辑说明先定义 SQL用 ? 占位符传学号调用 PreparedStatement 的 setString 给占位符赋值。这里不使用 Statement 拼接字符串是因为拼接容易导致 SQL 注入。JTable 本身不直接存数据它依赖 TableModel所以更新界面时先清空 model 再重新 addRow。参数说明sno 是登录时保存的学生号从登录窗口一路传过来。如果该学生在 sc 表里没有选课记录NOT IN 子查询返回空集合所有课程都会显示。选课按钮的监听器逻辑同理做 INSERTString insertSql INSERT INTO sc(sno, cno) VALUES(?, ?); PreparedStatement ps conn.prepareStatement(insertSql); ps.setString(1, sno); ps.setString(2, selectedCno); int rows ps.executeUpdate(); if (rows 0) { JOptionPane.showMessageDialog(this, 选课成功); // 重新加载课程列表 loadAvailableCourses(); }这里有个业务细节选课表 sc 的 score 字段在插入时没有赋值默认 NULL表示未考试。到成绩录入阶段教师端执行 UPDATE 而不是 INSERT。这也呼应了第 3 章的表结构设计——成绩是选课行为的后续状态变更不是独立实体。如果你在实现时误用了 INSERT 覆盖成绩会导致一个学生同一门课出现多条记录这是课设答辩时很容易被挑出的逻辑错误。4.3 成绩录入和查询一条 update 和一个 join 查询成绩录入界面里的 JTable 会先展示当前教师所授课程的选课学生名单教师直接在表格里填分或通过弹窗输入分数。保存的 SQL 事务性很强一般是一句 UPDATEString updateSql UPDATE sc SET score ? WHERE sno ? AND cno ?; PreparedStatement ps conn.prepareStatement(updateSql); ps.setDouble(1, 85.5); // 要录入的成绩 ps.setString(2, sno); // 学号 ps.setString(3, cno); // 课程号 int rows ps.executeUpdate(); if (rows 0) { // 如果影响行数为 0说明该学生没选这门课 JOptionPane.showMessageDialog(this, 该学生未选这门课程); }成绩查询的界面则是一个典型的三表联合查询。学生查询成绩时JTable 需要展示课程名、学分、成绩三列数据来自 course 和 sc 两张表的关联String querySql SELECT c.cname, c.credit, sc.score FROM course c JOIN sc ON c.cno sc.cno WHERE sc.sno ?; PreparedStatement ps conn.prepareStatement(querySql); ps.setString(1, sno); ResultSet rs ps.executeQuery(); // 遍历 rs把每行数据塞进 JTable while (rs.next()) { String cname rs.getString(cname); int credit rs.getInt(credit); Double score rs.getDouble(score); if (rs.wasNull()) { score null; // 尚未录入成绩显示“未考” } model.addRow(new Object[]{cname, credit, score}); }这段代码里的 rs.getDouble 必须在取值后立刻调用 wasNull 判断否则把 NULL 转成 double 会返回 0.0用户看到“0 分”就会误以为自己挂了科。这是 JDBC 里结果集空值处理的经典坑也面试里常考的点ResultSet 的 getXxx 方法如何感知 NULL 值。换句话说整个系统的核心就三条 SQL选课查 NOT IN、录入成绩 UPDATE、成绩查询 JOIN。把这三种 SQL 的写法吃透再去理解复杂的教务系统也不难。5. 避坑指南连接、编码、界面显示绕不过去的五个问题5.1 连接与驱动类问题这一节内容我从自己的调试记录里挑了三项都是拿到这套系统后最容易踩的。现象一运行项目后窗口还没弹出控制台报 SQLNonTransientConnectionException 或 Communications link failure。原因MySQL 驱动与数据库之间的 SSL 握手失败。MySQL 8 默认开启 SSL驱动 8.x 也会默认尝试 SSL 连接本地库通常没有配置有效证书。解决在 JdbcConfig 的连接 URL 上加 useSSLfalse同时加 allowPublicKeyRetrievaltrue这也对应了网络检索里常看到的 mysql ssl 连接错误jdbc:mysql://localhost:3306/stusystem?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai现象二报错信息是 java.sql.SQLException: The server time zone value й׼ʱ is unrecognized。原因MySQL 8 的时区配置没有显式指定而驱动要求提供 serverTimezone。解决在 URL 中加 serverTimezoneAsia/Shanghai。如果你拿到的源码里没写这个参数补上即可。时区参数一旦缺失同学借脚本跑别人电脑上时经常出现属于典型的“同一份代码在不同机器上结果不同”的坑。现象三用 Navicat 能连上 MySQL但程序报 Public Key Retrieval is not allowed。原因MySQL 8 默认的 caching_sha2_password 认证插件要求客户端允许获取服务端公钥。解决除了在 URL 加 allowPublicKeyRetrievaltrue还可以把数据库账号的认证插件改成 mysql_native_password但改插件会影响其他客户端连接我一般更倾向改 URL 参数不动数据库配置。5.2 数据与界面显示类问题现象四数据库表中中文显示正常但 Swing 界面上的中文变成乱码或方块。原因有两种可能第一是 SQL 脚本导入时使用的字符集和程序读取时不一致比如脚本是 utf8但导入时 Navicat 默认用了 gbk第二是 IDEA 运行环境的文件编码没设成 UTF-8。解决先看 SQL 脚本头部有没有 SET NAMES utf8mb4再通过 Navicat 的连接属性里把编码设为 utf8mb4IDEA 里在 Help Edit Custom VM Options 加上 -Dfile.encodingUTF-8然后在 File Settings Editor File Encodings 把项目编码和 Default encoding 都改成 UTF-8。重启 IDEA 后重新编译中文基本能恢复。如果界面仍乱码还要检查操作系统区域语言是否开了 Beta 版 UTF-8 选项。现象五按截图里的界面预览一切正常但自己电脑上窗口显示不全按钮错位甚至挤成一团。原因Swing 默认布局管理器在实际运行中受屏幕分辨率和 DPI 缩放影响。作者截图时用的分辨率可能和你不同。解决如果你只是课设交付优先把显示器缩放调成 100% 再运行如果想彻底改善用 GridBagLayout 替换掉绝对定位布局或者在 JFrame 创建时调用 setPreferredSize 控制组件尺寸。还有一条看似玄学但影响很大JdbcConfig 里连接 URL 中的数据库名必须和 SQL 脚本里 CREATE DATABASE 的名称一字不差。大小写不一致在 Windows 上可能不报错但在 Linux 上直接报 error 2002 (HY000) 或 Unknown database。原因就是数据库名大小写敏感。解决的方法是打开脚本确认库名统一成小写。我一般会直接告诉学生改成和 JdbcConfig 完全一致避免提交到不同系统后翻车。6. 进一步改造把课程设计升级成能写进简历的项目这套系统最大的提升空间不在界面而在数据访问层。当前代码用 JdbcConfig 静态方法返回 Connection每个 DAO 里自己创建 PreparedStatement这是课设的标准写法但投递简历时面试官看到这种代码很容易问一句“有没有用连接池”。我一般在拿到这类源码后会花半小时把 JDBC 层替换成 Druid 连接池改动不大但面试价值直接上升一个档次。首先是替换依赖把 lib 下的驱动留着再加一个 druid 的 jar 包。如果你用 Maven 重构直接在 pom.xml 里加依赖。然后新建一个 DruidConfigpackage com.service; import com.alibaba.druid.pool.DruidDataSourceFactory; import javax.sql.DataSource; import java.io.InputStream; import java.util.Properties; public class DruidConfig { private static DataSource ds; static { try { ClassLoader cl DruidConfig.class.getClassLoader(); InputStream in cl.getResourceAsStream(druid.properties); Properties props new Properties(); props.load(in); ds DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws Exception { return ds.getConnection(); } }druid.properties 放在 src 根目录下driverClassNamecom.mysql.cj.jdbc.Driver urljdbc:mysql://localhost:3306/stusystem?useSSLfalseserverTimezoneAsia/Shanghai usernameroot password123456 initialSize5 maxActive10 maxWait3000替换原 JdbcConfig 后DAO 类的调用方式不用变只是 getConnection 从连接池取连接而不是每次新建。这样的改动有两个直接好处第一避免频繁创建和销毁连接第二你可以在面试时说“我用过 Druid 连接池通过配置文件管理数据库资源”这是标准答案。连接池也是 java 面试题里最高频的考点之一理解它的原理之后你甚至可以顺手把这套系统的 DAO 层整理成可复用的模板。我自己的习惯是拿到任何课设源码都是先跑一遍原始版本确保功能完整再按“连接池 → 统一异常处理 → 日志输出”的顺序去改造而不是一上来就重写。有一次我直接去掉 JdbcConfig 换连接池结果因为没注意原代码在多个类里都用 Class.forName 注册驱动导致重复注册报错。从那以后我每次改数据访问层都会全局搜一遍 Class.forName 和 DriverManager强制走一遍调用链再动手。希望帮到你。本文还有配套的精品资源点击获取