
简介这是一份面向Java课程设计或数据库初学者的完整班级信息管理系统项目包基于JavaMySQL实现GUI界面涵盖登录、处理记录、查询记录、重置密码等典型功能模块适合用于毕业设计、课设参考或巩固JDBC与Swing/JavaFX开发技能。压缩包共57个文件包含9个Java源码、26个class编译文件、15个XML配置、1个MySQL驱动jar包及课程设计文档整体大小仅3MB轻量易用源码与class并存便于直接运行和二次开发XML中记录项目结构与环境配置。目前已有167人学习浏览说明该案例具备一定参考价值。通过该项目可以掌握Java GUI事件处理、JDBC数据库增删改查、SQL防注入等关键要点同时附带的README与课程设计文档能帮助理解整体设计思路适合快速上手并在此基础上扩展功能。1. 为什么班级信息管理系统值得用 Java MySQL 重写一遍很多课程设计做到最后就变成“能跑就行”Swing 拖一个窗口JDBC 拼几条 SQL老师验收完就压箱底。但如果你要把这个 Java MySQL 的班级信息管理系统写进简历或者准备接下来的 Java 面试你会发现它其实是一个特别好的 CRUD 样板——登录验证、GUI 事件分发、PreparedStatement 防注入、ResultSet 映射、MVC 拆分全都能在一个项目里练到。这个系统解决的是最典型的“人机交互 数据持久化”问题老师或班长通过图形界面录入班级和学生信息数据落到 MySQL查询和统计基于 SQL 完成。它也适合作为第一次完整接触 Java Swing 与 JDBC 的入门项目规模不大但足够看到真实业务系统的基本轮廓。2. 班级信息管理系统的登录鉴权与三大模块拆解2.1 功能模块划分与数据库表设计先定数据结构再写界面班级信息管理系统在功能上可以拆成三块处理记录、查询记录、重置密码。加上最外层的登录鉴权整个系统就是一张清晰的功能表。我习惯先从模块表反推数据库表而不是先画界面因为 Swing 布局改起来容易数据库字段一旦漏掉后面所有 SQL 都要跟着改。功能模块主要操作涉及数据库表用户身份登录界面用户名、密码校验admin未登录主界面功能导航、显示当前用户admin已登录处理记录添加、修改、删除学生记录student管理员查询记录按姓名、班级、学号查询student管理员重置密码更新管理员密码admin管理员建表语句建议直接执行下面这段 SQL。注意把utf8mb4显式写出来否则后面中文乱码会非常折磨人。CREATE DATABASE IF NOT EXISTS class_manager CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE class_manager; CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL UNIQUE, name VARCHAR(32) NOT NULL, gender VARCHAR(4), class_name VARCHAR(32), score DECIMAL(5,2), phone VARCHAR(20), remark VARCHAR(255) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO admin (username, password) VALUES (admin, 123456);这个设计的几个关键点student_no加上UNIQUE约束防止同一个学号被重复录入DECIMAL(5,2)用来存成绩能保证两位小数比DOUBLE更安全admin表单独建是因为重置密码只需要这一个表不需要和学生表耦合。课程设计里最常见的错误就是把密码字段写成VARCHAR(20)然后把明文直接存进去这里至少给password留到 64 位为后续做 SHA-256 做准备。2.2 登录鉴权从文本框到数据库校验登录界面的本质是“取到输入框的值和数据库里的记录做一次等值匹配”。难点不在 SQL而在 Swing 的线程模型按钮点击事件里不能直接调用可能阻塞的 JDBC 操作否则界面会卡死。常见做法是开一个匿名线程去做数据库校验校验完成后再通过SwingUtilities.invokeLater回到事件分发线程更新界面。loginButton.addActionListener(e - { String username userField.getText().trim(); String password new String(passField.getPassword()); new Thread(() - { boolean ok login(username, password); SwingUtilities.invokeLater(() - { if (ok) { JOptionPane.showMessageDialog(frame, 登录成功); frame.dispose(); openMainWindow(username); } else { JOptionPane.showMessageDialog(frame, 用户名或密码错误); } }); }).start(); });这里有几个参数细节值得注意passField.getPassword()返回的是char[]不是String把它转成字符串后要马上使用不要长期留在内存里new String(passField.getPassword())是默认平台的编码方式如果你改了 JVM 启动参数最好显式指定StandardCharsets.UTF_8。userField.getText().trim()去掉首尾空格避免用户在输入框里多敲一个空格导致登录失败。校验线程里不要访问任何 Swing 组件只返回布尔结果否则会出现并发修改异常。2.3 主界面的导航与事件分发登录成功之后进入主界面主界面通常是一个JFrame里面放菜单栏或者按钮组。这个项目的设计里主界面要承载三个入口处理记录、查询记录、重置密码。我建议用CardLayout做页面切换而不是每个按钮弹出新窗口因为新窗口多了以后用户很容易迷失而且子窗口关闭时如果不彻底释放资源程序里到处都是隐藏的JFrame。JPanel cardPanel new JPanel(new CardLayout()); cardPanel.add(recordPanel, record); cardPanel.add(queryPanel, query); cardPanel.add(passwordPanel, password); CardLayout cl (CardLayout) cardPanel.getLayout(); recordButton.addActionListener(e - cl.show(cardPanel, record)); queryButton.addActionListener(e - cl.show(cardPanel, query)); passwordButton.addActionListener(e - cl.show(cardPanel, password));CardLayout是 Swing 里做多页面切换最轻量的方案。add(面板, 标识)里的第二个字符串就是后续show时用的 key建议用驼峰命名别用中文避免编码问题。这种做法的好处是三个模块共享同一个JFrame内存占用低而且各个模块的数据刷新只需要在切换页面时调一次refresh()方法。2.4 处理记录与查询记录的设计边界处理记录模块负责增删改查询记录模块负责查这两个模块在 SQL 上很容易写串。我的建议是增删改用固定 SQL 加绑定参数查询用动态拼接 SQL 但必须限制拼接条件。比如按姓名模糊查询时如果用户没有输入关键字就不应该拼上去保持SELECT * FROM student WHERE 11作为兜底后面再按条件追加。重置密码模块要注意的是修改密码前一定要校验旧密码否则任何一个拿到主界面的人都能把管理员密码改掉。代码上就是先查一次admin表拿输入的旧密码和查询到的密码做比对匹配成功才执行UPDATE admin SET password? WHERE id?。这三个模块都要求事务边界清晰单条增删改不需要手动开事务但重置密码和后续可能加上的批量导入就必须放到同一个事务里。3. 从零搭建可运行的 Java Swing MySQL 项目3.1 环境准备JDK、MySQL 与驱动包版本对齐照着网上的 MySQL 安装配置教程装好数据库之后最容易翻车的是驱动版本。这个项目带的是mysql-connector-java-5.1.39-bin.jar对应 MySQL 5.x 和 5.7 都没问题但如果你本地装的是 MySQL 8.0 以上这个旧驱动虽然也能连但必须把驱动类名改成com.mysql.cj.jdbc.Driver并且连接 URL 里要额外加上serverTimezoneAsia/Shanghai。推荐直接用项目自带的驱动包把它放到lib目录并加入 IDE 的 Build Path。命令行编译时用java -cp .;mysql-connector-java-5.1.39-bin.jar这样的写法。这里有个常见环境坑Windows 下-cp的分隔符是分号Linux/macOS 下是冒号写脚本时一定要分开。3.2 JDBC 工具类封装与连接参数说明整个系统所有模块都要连接数据库所以第一步应该封装一个DBUtil把驱动加载、连接创建、资源释放都收敛到一处。这段代码是这个项目里复用率最高的部分也是面试时 Java 基础考点里经常被追问的地方。package util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL jdbc:mysql://127.0.0.1:3306/class_manager ?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/Shanghai; private static final String USER root; private static final String PASSWORD 你的密码; static { try { Class.forName(com.mysql.jdbc.Driver); } catch (ClassNotFoundException e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }useSSLfalse是在本地开发时关掉 SSL 加密提高连接速度如果部署到公网建议改成true并配置证书。characterEncodingutf8解决的是客户端写入数据的编码问题配合建库时的utf8mb4一起使用。serverTimezoneAsia/Shanghai是 MySQL 8 时区处理的关键旧驱动不加也能跑但报时区异常时第一时间要想起它。3.3 学生记录的增删改查实现处理记录模块的核心是 PreparedStatement 的增删改。以添加学生为例写一个独立的StudentDAO类对外暴露addStudent方法。public boolean addStudent(Student s) { String sql INSERT INTO student (student_no, name, gender, class_name, score, phone, remark) VALUES (?,?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, s.getStudentNo()); ps.setString(2, s.getName()); ps.setString(3, s.getGender()); ps.setString(4, s.getClassName()); ps.setBigDecimal(5, s.getScore()); ps.setString(6, s.getPhone()); ps.setString(7, s.getRemark()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }这里所有字段都用setString或setBigDecimal填充而不是拼进 SQL 字符串。setBigDecimal(5, s.getScore())要求实体类里的score是BigDecimal类型和数据库的DECIMAL(5,2)严格对应。executeUpdate()返回受影响的行数大于 0 说明插入成功。注意try-with-resources写法会在代码块结束后自动关闭Connection和PreparedStatement不用手动写rs.close()和conn.close()少犯资源泄漏的错。删除和修改的结构完全一致只有 SQL 不同。删除用id定位修改用id加上需要变更的字段。唯一要注意的是修改操作最好带上乐观锁版本号但课程设计阶段用id 旧值做WHERE判断也足够了。查询模块的难点是条件组合。用户可能只输入姓名也可能同时输入班级和学号。动态拼接 SQL 时必须用ListObject保存参数最后统一绑定。public ListStudent queryStudents(String nameKeyword, String className) { StringBuilder sql new StringBuilder(SELECT * FROM student WHERE 11); ListObject params new ArrayList(); if (nameKeyword ! null !nameKeyword.isEmpty()) { sql.append( AND name LIKE ?); params.add(% nameKeyword %); } if (className ! null !className.isEmpty()) { sql.append( AND class_name ?); params.add(className); } 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(); ListStudent list new ArrayList(); while (rs.next()) { Student s new Student(); s.setId(rs.getInt(id)); s.setStudentNo(rs.getString(student_no)); s.setName(rs.getString(name)); s.setClassName(rs.getString(class_name)); s.setScore(rs.getBigDecimal(score)); list.add(s); } return list; } catch (SQLException e) { e.printStackTrace(); return new ArrayList(); } }WHERE 11这个写法在动态 SQL 里非常常见它并不是真的去判断 1 等于 1而是为了让后面每一个AND条件都能安全追加不需要额外判断是否是第一个条件。ps.setObject(i 1, params.get(i))里的下标从 1 开始和 JDBC 参数的占位符索引一致。LIKE 查询要在传参时手动拼%否则只起到等值匹配的作用。3.4 把查询结果渲染到 JTable查询出来的 List 不能直接显示需要套上JTable的TableModel。我一般用DefaultTableModel把 List 里的每个对象转成Object[]再添加到模型里。DefaultTableModel model new DefaultTableModel( new String[]{学号, 姓名, 班级, 成绩}, 0 ); for (Student s : list) { model.addRow(new Object[]{ s.getStudentNo(), s.getName(), s.getClassName(), s.getScore() }); } table.setModel(model);DefaultTableModel第一个参数是表头数组第二个参数是初始行数。addRow每次追加一行所以查询前必须先setRowCount(0)清空旧数据。这里有个性能细节如果数据量超过 1000 行addRow每加一行都会触发表格重绘这时应该一次性把所有行放到Vector里再传入模型或者直接重写TableModel。课程设计的数据量通常不大但面试官可能会追问这个点。4. 课程设计答辩中的高频问题与 MySQL 连接排错实战4.1 中文乱码的根源与处理中文乱码是 Swing 课程设计里出现频率最高的问题。表现是数据库里看到的内容正常但界面上显示成??或者反过来。根源在于连接 URL、数据库字符集、表格显示编码三者不一致。排查路径依次是先看 MySQL 服务端字符集执行SHOW VARIABLES LIKE character_set%;确认character_set_server是utf8mb4再看 JDBC URL 是否带characterEncodingutf8最后看 Swing 页面是否使用了中文默认字体。最容易被忽略的是mysql-connector-java-5.1.39这个版本它需要characterEncodingutf8而不能是utf8mb4因为旧驱动不认识utf8mb4会抛Unsupported character encoding utf8mb4。解决方式是在数据库连接 URL 和建表语句里统一使用utf8mb4但驱动侧的参数保持为utf8两者对应同一个编码体系不会冲突。4.2 SQL 注入的防线PreparedStatement 与参数绑定很多 Java 面试八股文里都会问 Statement 和 PreparedStatement 的区别。在这个项目里最现实的回答是PreparedStatement 用预编译的占位符取代字符串拼接攻击者输入的 OR 11会被当作普通字符串参数而不是 SQL 结构的一部分。// 错误示范直接拼接 String sql SELECT * FROM student WHERE name name ; // 正确示范占位符绑定 String sql SELECT * FROM student WHERE name ?;面试时还可以补充一点PreparedStatement对数据库还有预编译缓存的效果同一条 SQL 多次执行时不需要重新解析语法树所以批量执行时性能也更好。这个项目里所有查询和修改都应该走 PreparedStatement即便学生表没有敏感数据也应该养成这个习惯。4.3 MySQL 连接失败的排错命令与日志连接失败时不要急着改代码先用命令行工具定位是哪一层出了问题。Windows 下可以执行netstat -ano | findstr 3306确认 MySQL 监听端口Linux 下用ss -lntp | grep 3306。如果是Access denied用账号密码手动登录 MySQL 测试权限如果是CommunicationsException检查 MySQL 服务是否启动。报错关键字常见原因排查动作ClassNotFoundException驱动 jar 没引入确认 Build Path 中有 mysql-connector 包Communications link failureMySQL 未启动或端口错误netstat -ano | findstr 3306Access denied for user用户名或密码错误命令行登录确认Unknown database数据库名拼错SHOW DATABASES;查看实际库名Public Key Retrieval is not allowedMySQL 8 缓存密钥问题在 URL 后加allowPublicKeyRetrievaltrue每解决一个问题建议把当时的堆栈异常和解决动作记进 README答辩时老师问“你有遇到过什么坑”就能直接说出一个具体场景。这里还有个隐藏点mysql-connector-java-5.1.39的驱动类和 MySQL 8 的caching_sha2_password认证插件不完全兼容如果报Public Key Retrieval is not allowed优先升级驱动而不是改数据库认证方式。4.4 课程设计答辩常见追问老师最喜欢问三个问题登录密码是怎么校验的删除学生记录时如果数据库出错怎么办多个用户同时操作会不会有问题。前两个可以直接从代码里找到答案第三个才是拉分点。多用户同时操作时你的DBUtil每次调用都创建一个新连接这必然导致连接开销大而且两条 SQL 之间没有事务保护。比如在“删除学生并更新班级人数”的场景里删除成功但更新失败数据就不一致了。这就要用到下一章的 DAO 分层和事务控制。5. 把课程设计升级为可维护的小型系统模块化、日志与事务5.1 用 DAO 层隔离 JDBC 代码当前代码里界面类和 SQL 混在一起改一个字段要从界面类翻到工具类。更好的做法是把数据库操作全部收敛到StudentDAO和AdminDAO界面层只调用 DAO 接口。具体来说StudentService负责业务校验比如成绩范围、学号非空StudentDAO只负责执行 SQL。界面代码里看不到任何Connection和PreparedStatement这样以后把数据源换成 MyBatis 或者 Spring JDBC界面都不用动。5.2 给关键操作加上事务边界重置密码、批量导入学生记录这些操作必须放在同一个事务里。JDBC 默认每次执行都自动提交所以要手动调整事务提交模式。public boolean resetPassword(String username, String oldPwd, String newPwd) { String checkSql SELECT password FROM admin WHERE username ?; String updateSql UPDATE admin SET password ? WHERE username ?; try (Connection conn DBUtil.getConnection()) { conn.setAutoCommit(false); PreparedStatement checkPs conn.prepareStatement(checkSql); checkPs.setString(1, username); ResultSet rs checkPs.executeQuery(); if (!rs.next() || !rs.getString(password).equals(oldPwd)) { conn.rollback(); return false; } PreparedStatement updatePs conn.prepareStatement(updateSql); updatePs.setString(1, newPwd); updatePs.setString(2, username); updatePs.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { try (Connection conn DBUtil.getConnection()) { conn.rollback(); } catch (SQLException ignored) {} e.printStackTrace(); return false; } }注意setAutoCommit(false)后面的所有 SQL 都必须在commit()或rollback()之后才算结束。这里我把日志输出加在了catch块里用java.util.logging记录完整堆栈比e.printStackTrace()更适合写入文件。日志文件不需要额外依赖Java 原生的Logger就能完成。事务控制这块还可以进一步优化把Connection放到线程局部变量ThreadLocal里保证一次业务调用全程使用同一个连接。课程设计阶段手动调用commit/rollback已经足够了但实现ThreadLocal的版本可以当作扩展点写进 README。5.3 排序和分页的简单实现如果学生数据超过几百条JTable 渲染就会变慢。可以给查询 SQL 加上ORDER BY student_no LIMIT ?,?。分页参数用LIMIT 0,50表示从第 0 条开始取 50 条。注意 MySQL 的分页参数不能直接拼字符串要用PreparedStatement绑定String sql SELECT * FROM student ORDER BY student_no LIMIT ?,?; ps.setInt(1, offset); ps.setInt(2, pageSize);实际答辩中老师可能会追问“如果用户点击表头排序怎么办”你可以回答通过监听TableColumnModel的列点击事件动态改变 ORDER BY 字段。实现起来不复杂但能体现你对 GUI 和 SQL 两层交互的理解。最后验证整个项目是否健康除了跑通登录和增删改还要做一次异常路径测试在 MySQL 停掉的情况下打开系统看界面是友好提示还是崩溃。把DBUtil里的连接失败包装成自定义异常在界面层统一捕获弹出一句“数据库连接失败请检查服务状态”比堆栈打满终端专业得多。在提交作业之前把连接 URL 中的serverTimezone和allowPublicKeyRetrieval参数按当前 MySQL 版本设置对然后重新编译运行一遍完整流程。本文还有配套的精品资源点击获取