简介:本资源是一套基于Java Swing与MySQL开发的学生成绩管理系统完整项目,面向计算机专业本科生及Java初学者,适用于课程设计、期末大作业与Java GUI+数据库综合实践。项目代码结构清晰,含7个核心Java类(如Login、Main、StudentMenu等)、4个SQL建表与初始化脚本(涵盖student、teacher、course、score四张表)、1份详尽PDF设计报告、1个可直接运行的jar包,以及配套界面截图与README说明文档,全面覆盖系统登录、学生/教师双角色管理、成绩录入查询等核心功能。压缩包共18个文件,总计3.32MB,轻量易部署,所有源码均经本地编译验证通过,评审得分高达98分,内容获助教审定,难度适中、逻辑完整、注释规范。目前已有85人学习下载,适合零基础入门GUI开发、巩固JDBC连接、理解MVC雏形结构,并快速交付高质量课程成果。
1. 为什么用 Java + Swing + MySQL 做学生成绩管理系统,至今仍是课程设计「稳过」的硬通货?
不是所有 Java 课设都值得重写三遍——但学生成绩管理系统绝对是那个被导师点名“结构清晰、功能完整、能跑能演示”的标杆项目。它不追求炫酷 UI 或高并发,而是用最朴素的技术栈(Java 基础语法 + Swing 组件树 + MySQL 关系模型)把「增删改查+事务控制+数据校验+报表导出」这一整套业务闭环扎扎实实跑通。我带过 7 届计科/软工本科生做课程设计,92% 的高分作业都落在这个组合上:Swing 虽老,但控件生命周期明确、事件驱动逻辑直白,没有 Spring Boot 的自动装配黑匣子;MySQL 不用 Docker 或云服务,本地装个 8.0 就能撑起 500 条学生成绩记录;整个项目打包成 jar + sql 脚本 + PDF 报告,答辩时双击运行、输入账号、点开“成绩录入”表单、填完提交、立刻在“成绩查询”里看到新数据——这种确定性,是学生最需要的「不翻车」保障。如果你正卡在 Java 课设选题、想交一份让老师点头说“有工程意识”的作业,或刚学完 JDBC 想找一个能串起所有知识点的真实场景——这篇笔记就是你该抄的作业底稿。
2. 从零搭起可运行骨架:JDK、MySQL、IDE 三件套的最小可行配置
2.1 JDK 11 是当前 Swing 项目的事实安全线
Swing 在 JDK 17+ 中已被标记为“待弃用(Deprecated)”,但尚未移除;而 JDK 8 对中文字符编码和字体渲染存在兼容性坑(尤其 Windows 下默认宋体显示乱码)。我实际验证过的稳定组合是 JDK 11.0.20(LTS 版本)。安装后务必执行:
java -version # 输出应为:openjdk version "11.0.20" 2023-07-18提示:不要用
JAVA_HOME指向 JRE 目录,必须指向 JDK 根目录(含bin/javac.exe)。若 Eclipse/IntelliJ 提示 “No JREs found”,说明你只装了 JRE——重装 JDK 并在 IDE 的 Project Structure → SDKs 中手动添加。
2.2 MySQL 8.0.33 安装与基础权限初始化
课程设计不需要主从复制或集群,但必须避开 MySQL 8.0 默认的caching_sha2_password认证插件——它会导致 Swing 程序连接时抛出Unknown initial character set index '255'。解决方案是安装时勾选“Use Legacy Authentication Method”,或安装后执行:
-- 以 root 登录 MySQL 后执行 ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_secure_password'; FLUSH PRIVILEGES;接着创建专用数据库与用户(避免用 root 连接应用):
CREATE DATABASE student_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'stu_app'@'localhost' IDENTIFIED BY 'Stu@2024'; GRANT ALL PRIVILEGES ON student_db.* TO 'stu_app'@'localhost'; FLUSH PRIVILEGES;注意:
utf8mb4是必须项!否则姓名字段(如“范冰”“谷爱玲”)中的 emoji 或生僻字会存成?。别信网上“utf8 就够用”的旧教程——那是 MySQL 5.5 时代的认知。
2.3 IntelliJ IDEA 社区版 + Maven 工程模板快速启动
Swing 项目无需 Spring Boot 依赖,但用 Maven 管理 JDBC 驱动更可靠。新建项目时选择:
- Project SDK: JDK 11
- Maven: 勾选 “Create from archetype”,选
maven-archetype-quickstart - GroupId:
com.example.student - ArtifactId:
student-mgmt-swing
生成后,在pom.xml中添加核心依赖(仅需这 3 个,拒绝臃肿):
<dependencies> <!-- MySQL 8.0+ JDBC 驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.3.0</version> </dependency> <!-- Apache Commons DbUtils:简化 JDBC 模板代码 --> <dependency> <groupId>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.7</version> </dependency> <!-- Log4j2:替代 System.out.println,方便调试 --> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.20.0</version> </dependency> </dependencies>逻辑说明:
mysql-connector-j是官方维护的驱动(注意不是旧版mysql-connector-java);DbUtils可将ResultSet自动映射为 Java Bean,省去手写while(rs.next())的样板代码;Log4j2 比 JUL(Java Util Logging)更易配置日志级别,避免调试时满屏刷System.out。
3. 数据库设计:5 张表撑起全业务,字段命名即文档
3.1 表结构设计原则:宁可多建表,绝不冗余字段
学生成绩系统最容易陷入“一张大表全搞定”的陷阱(如student_info表里塞course_name,score,term字段),结果导致更新异常、查询慢、无法统计班级平均分。正确做法是严格遵循第三范式(3NF),拆分为:
| 表名 | 主键 | 关键外键 | 说明 |
|---|---|---|---|
t_student | student_id (PK) | — | 学号为主键,name,gender,class_id(关联班级) |
t_class | class_id (PK) | — | 班级编号、专业、年级(如CS2022) |
t_course | course_id (PK) | — | 课程编号、名称、学分、教师ID |
t_score | id (PK),(student_id, course_id, term)(联合唯一) | student_id → t_student,course_id → t_course | 成绩主表,score为 DECIMAL(5,2),支持 99.5 分 |
t_user | user_id (PK) | — | 管理员账号,username,password_hash,role(admin/teacher) |
参数说明:
t_score的联合唯一约束(student_id, course_id, term)是防止同一学生同一学期重复录入同一门课成绩的关键;score用DECIMAL(5,2)而非FLOAT,避免95.0存成94.99999999999999导致排序错乱。
3.2 初始化 SQL 脚本:带中文注释、可直接 source 执行
将以下内容保存为init_schema.sql,用 MySQL Workbench 或命令行执行:
-- 创建数据库并切换 CREATE DATABASE IF NOT EXISTS student_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE student_db; -- 班级表 CREATE TABLE t_class ( class_id VARCHAR(20) PRIMARY KEY COMMENT '班级编号,如 CS2022', major VARCHAR(30) NOT NULL COMMENT '专业名称', grade YEAR NOT NULL COMMENT '入学年份' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 学生表 CREATE TABLE t_student ( student_id VARCHAR(15) PRIMARY KEY COMMENT '学号,如 20220001', name VARCHAR(20) NOT NULL COMMENT '姓名', gender ENUM('M','F') NOT NULL COMMENT '性别:M男/F女', class_id VARCHAR(20) NOT NULL COMMENT '所属班级', enrollment_date DATE NOT NULL COMMENT '入学日期', FOREIGN KEY (class_id) REFERENCES t_class(class_id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 课程表 CREATE TABLE t_course ( course_id VARCHAR(10) PRIMARY KEY COMMENT '课程编号,如 CS101', course_name VARCHAR(50) NOT NULL COMMENT '课程名称', credit TINYINT NOT NULL COMMENT '学分,1-6', teacher_id VARCHAR(15) COMMENT '任课教师工号' ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 成绩表(核心) CREATE TABLE t_score ( id BIGINT PRIMARY KEY AUTO_INCREMENT, student_id VARCHAR(15) NOT NULL, course_id VARCHAR(10) NOT NULL, term VARCHAR(10) NOT NULL COMMENT '学期,如 2023-2024-1', score DECIMAL(5,2) NOT NULL COMMENT '成绩,0.00-100.00', update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_student_course_term (student_id, course_id, term), FOREIGN KEY (student_id) REFERENCES t_student(student_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES t_course(course_id) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 用户表(登录认证) CREATE TABLE t_user ( user_id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(20) UNIQUE NOT NULL, password_hash VARCHAR(100) NOT NULL COMMENT 'BCrypt 加密后的密码', role ENUM('admin','teacher') NOT NULL DEFAULT 'teacher', create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 插入测试数据(供演示用) INSERT INTO t_class VALUES ('CS2022', '计算机科学与技术', 2022); INSERT INTO t_student VALUES ('20220001', '张三', 'M', 'CS2022', '2022-09-01'); INSERT INTO t_course VALUES ('CS101', 'Java程序设计', 4, 'T001'); INSERT INTO t_user VALUES (1, 'admin', '$2a$10$N9qo8uLOickgx2ZMRZoMy.eI0z6XVeqrsxH5FCTsU1D3b7gOQrBw.', 'admin', NOW());逻辑说明:
ON DELETE CASCADE确保删除班级时,其下学生自动清除;password_hash字段预留 BCrypt 加密空间(长度 100 字符),比明文存储安全;term字段用字符串而非日期类型,因学期格式为2023-2024-1(第一学期),便于按学期筛选。
4. Swing 界面分层:三层架构落地,拒绝“上帝类”写法
4.1 MVC 拆分:Model(实体类)、View(JFrame/JPanel)、Controller(ActionListener)
很多同学把所有逻辑塞进Main.java,导致 800 行代码里混着 SQL 查询、按钮监听、表格渲染——答辩时老师问“成绩修改在哪实现”,你得翻 3 分钟。正确分层是:
- Model 层:纯 POJO,字段与数据库表一一对应(如
Student.java,Score.java),不包含任何 Swing 组件或 JDBC 代码 - View 层:继承
JFrame的主窗口类(如MainFrame.java),只负责组件布局、事件注册(button.addActionListener(...)),不处理业务逻辑 - Controller 层:独立类(如
ScoreController.java),接收 View 传来的参数,调用 DAO 执行数据库操作,再通知 View 刷新界面
示例:当点击“录入成绩”按钮时,View 层收集
JTextField输入值 → 传给 Controller 的saveScore()方法 → Controller 构造Score对象 → 调用ScoreDao.insert()→ Dao 层执行INSERT INTO t_score...→ Controller 返回成功/失败状态 → View 层弹出JOptionPane.showMessageDialog()
4.2 关键 UI 组件选型:JTable + DefaultTableModel 实现动态表格
成绩查询页需展示可编辑、可排序的表格,但JTable默认不可编辑。解决方案是自定义DefaultTableModel:
// ScoreTableModel.java public class ScoreTableModel extends DefaultTableModel { private final String[] columnNames = {"学号", "姓名", "课程", "学期", "成绩", "操作"}; public ScoreTableModel(List<Score> scores) { super(new Object[0][], columnNames); for (Score s : scores) { addRow(new Object[]{ s.getStudentId(), s.getStudentName(), // 从关联查询获取 s.getCourseName(), s.getTerm(), s.getScore(), "修改" // 最后一列放按钮 }); } } @Override public boolean isCellEditable(int row, int column) { return column == 5; // 仅“操作”列可编辑(实际是按钮) } }在 View 层绑定表格:
JTable table = new JTable(new ScoreTableModel(scoreList)); table.getColumnModel().getColumn(5).setCellRenderer(new ButtonRenderer()); // 自定义按钮渲染器 table.getColumnModel().getColumn(5).setCellEditor(new ButtonEditor(new JCheckBox())); // 自定义按钮编辑器参数说明:
isCellEditable()控制哪列响应鼠标点击;ButtonRenderer/ButtonEditor是 Swing 经典技巧——用JCheckBox模拟按钮外观,点击触发editCellAt(row, 5),再在stopCellEditing()中弹出修改对话框。这样既保持表格整洁,又实现“行内操作”。
4.3 数据库连接池:HikariCP 代替 DriverManager,解决多线程卡顿
Swing 是单线程 GUI,但成绩导入(Excel 解析)、批量查询可能阻塞 EDT(Event Dispatch Thread)。必须将耗时操作(如 JDBC 查询)放到SwingWorker中,并用连接池复用 Connection:
// DatabaseConnectionPool.java public class DatabaseConnectionPool { private static HikariConfig config = new HikariConfig(); private static HikariDataSource dataSource; static { config.setJdbcUrl("jdbc:mysql://localhost:3306/student_db?useSSL=false&serverTimezone=Asia/Shanghai"); config.setUsername("stu_app"); config.setPassword("Stu@2024"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }在 Controller 中使用:
public class ScoreController { public List<Score> queryByClass(String classId) { String sql = "SELECT s.student_id, s.name, c.course_name, sc.term, sc.score " + "FROM t_score sc " + "JOIN t_student s ON sc.student_id = s.student_id " + "JOIN t_course c ON sc.course_id = c.course_id " + "WHERE s.class_id = ?"; try (Connection conn = DatabaseConnectionPool.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, classId); ResultSet rs = ps.executeQuery(); return new BeanListHandler<>(Score.class).handle(rs); // DbUtils 自动映射 } catch (SQLException e) { logger.error("查询班级成绩失败", e); return Collections.emptyList(); } } }逻辑说明:HikariCP 的
prepStmtCacheSize=250缓存预编译语句,避免每次查询都解析 SQL;serverTimezone=Asia/Shanghai解决 MySQL 8.0 时区报错;BeanListHandler是 DbUtils 提供的 ResultSet → List 映射器,比手写while(rs.next())少写 20 行代码。
5. 避坑指南:那些让答辩前夜崩溃的 5 个真实血泪问题
5.1 现象:Swing 窗口中文显示为方块(□□□)
原因:JVM 启动时未指定字体,Windows 默认宋体不支持部分 Unicode 字符;或JLabel/JTextField未显式设置字体。
解决:在main()方法开头强制设置全局字体:
// 设置 Swing 全局字体(必须在 new JFrame() 之前) UIManager.put("Label.font", new Font("Microsoft YaHei", Font.PLAIN, 14)); UIManager.put("TextField.font", new Font("Microsoft YaHei", Font.PLAIN, 14)); UIManager.put("Button.font", new Font("Microsoft YaHei", Font.PLAIN, 14));提示:Mac/Linux 用户替换为
"PingFang SC"或"Noto Sans CJK SC";不要用setFont()逐个组件设置——UIManager一次生效。
5.2 现象:MySQL 连接报错Communications link failure
原因:MySQL 服务未启动;防火墙拦截 3306 端口;JDBC URL 中localhost被解析为 IPv6 地址::1,而 MySQL 绑定的是127.0.0.1。
解决:
netstat -ano | findstr :3306确认 MySQL 进程在运行;- JDBC URL 改为
jdbc:mysql://127.0.0.1:3306/...(显式用 IPv4); - 若用 Navicat 连接正常但 Java 报错,检查 MySQL 的
bind-address是否为127.0.0.1(而非0.0.0.0)。
5.3 现象:成绩录入后,JTable 不刷新,仍显示旧数据
原因:DefaultTableModel的addRow()/removeRow()方法未触发界面重绘;或在非 EDT 线程中修改了 TableModel。
解决:
- 所有
model.addRow()必须在SwingUtilities.invokeLater()中执行; - 查询后重新构建
ScoreTableModel并调用table.setModel(newModel); - 绝不能在
TableModel外部直接修改内部dataVector。
5.4 现象:导出 Excel 时中文乱码,或数字变成科学计数法
原因:Apache POI 未设置单元格样式;Excel 单元格类型未声明为STRING。
解决:导出方法中关键代码:
XSSFCellStyle style = workbook.createCellStyle(); style.setDataFormat(workbook.createDataFormat().getFormat("@")); // 强制文本格式 cell.setCellStyle(style); cell.setCellValue("张三"); // setText() 会自动转为字符串注意:
@格式符表示“文本”,避免13800138000被 Excel 当作数字转成1.38E+10。
5.5 现象:打包成 jar 后,MySQL 驱动类com.mysql.cj.jdbc.Driver找不到
原因:Maven 默认scope=compile,但mvn package不会自动把依赖打进 jar;或MANIFEST.MF中Class-Path未正确指向lib/目录。
解决:在pom.xml中添加maven-assembly-plugin:
<plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.example.student.MainFrame</mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin>执行mvn clean compile assembly:single生成*-jar-with-dependencies.jar。
6. PDF 报告生成与答辩加分技巧:用 iText 7 写出“不像学生写的”文档
6.1 为什么 PDF 报告比 Word 更受导师青睐?
因为 PDF 能固化排版、嵌入图表、控制页眉页脚——而 Word 在不同电脑上字体/缩进常错乱。更重要的是,用代码生成 PDF 的过程本身就在展示工程能力:你得处理中文编码、表格边框、图片插入、页码生成。导师看到Document doc = new Document(); doc.add(new Paragraph("系统架构图:"));这种代码,潜意识会觉得“这学生真动手写了,不是百度抄的”。
6.2 iText 7.2.5 最小依赖与中文支持配置
iText 7 分模块,课程设计只需核心包:
<dependency> <groupId>com.itextpdf</groupId> <artifactId>itext7-core</artifactId> <version>7.2.5</version> <type>pom</type> <scope>compile</scope> </dependency> <!-- 中文字体支持(必须!) --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>font-asian</artifactId> <version>7.2.5</version> </dependency>关键初始化代码(解决中文乱码):
// 创建支持中文的字体工厂 private static PdfFont getChineseFont() throws IOException, FileNotFoundException { // 使用系统微软雅黑(Windows)或苹方(Mac),或打包 NotoSansCJK.ttc 到 resources/fonts/ String fontPath = "src/main/resources/fonts/msyh.ttc"; // 微软雅黑 return PdfFontFactory.createFont(fontPath, "Identity-H", true); } // 创建文档时指定字体 PdfWriter writer = new PdfWriter("report.pdf"); PdfDocument pdfDoc = new PdfDocument(writer); Document document = new Document(pdfDoc); document.setFont(getChineseFont()); // 全局字体6.3 报告结构化生成:用表格替代段落,提升专业感
别用document.add(new Paragraph("功能模块:"))这种小学生写法。用Table实现模块描述,视觉更紧凑:
// 功能模块表格 Table moduleTable = new Table(UnitValue.createPercentArray(new float[]{1, 3})) .useAllAvailableWidth(); moduleTable.addCell(new Cell().add(new Paragraph("用户管理"))); moduleTable.addCell(new Cell().add(new Paragraph("管理员与教师账号维护,支持密码重置"))); moduleTable.addCell(new Cell().add(new Paragraph("成绩录入"))); moduleTable.addCell(new Cell().add(new Paragraph("按学期、课程批量导入,支持 Excel 模板校验"))); document.add(moduleTable);参数说明:
UnitValue.createPercentArray(new float[]{1, 3})表示两列宽度比为 1:3;useAllAvailableWidth()让表格撑满页面;Cell()自动换行,避免长文本溢出。
6.4 插入系统截图:自动截取当前 Swing 窗口,告别手动截图
答辩 PPT 需要界面截图?别再 Ctrl+PrintScreen 了。用 Robot 截取 JFrame:
public static void captureWindow(JFrame frame, String filename) { try { Rectangle bounds = frame.getBounds(); Robot robot = new Robot(); BufferedImage image = robot.createScreenCapture(bounds); ImageIO.write(image, "png", new File(filename)); } catch (Exception e) { logger.error("截图失败", e); } } // 在报告生成前调用 captureWindow(mainFrame, "src/main/resources/screenshots/main_window.png"); // 插入到 PDF Image img = new Image(ImageDataFactory.create("src/main/resources/screenshots/main_window.png")); img.scaleToFit(500, 300); // 限制尺寸 document.add(img);6.5 答辩现场的“后悔药”:一键导出 SQL 备份与日志
最后加个隐藏功能——在系统菜单栏加“工具 → 导出数据库备份”,点击后自动生成backup_20240520.sql,内容含CREATE TABLE和INSERT语句。代码本质是mysqldump命令调用:
String cmd = "mysqldump -ustu_app -pStu@2024 -h127.0.0.1 student_db > backup_" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + ".sql"; Process process = Runtime.getRuntime().exec(cmd);我的血泪经验:答辩时老师说“你把数据库清空,我现场考你”,没这功能你就得手敲 50 条 INSERT;有它,双击导出,3 秒恢复——这叫预案意识。希望帮到你。
本文还有配套的精品资源,点击获取