首页 / 资讯中心 / 文章详情

Java GUI+MySQL学生信息管理系统源码解析:建库、连接与避坑指南

Java GUI+MySQL学生信息管理系统源码解析:建库、连接与避坑指南 ★ FEATURED ARTICLE
简介一份基于Java GUI与MySQL的学生信息管理系统完整源码包适合Java初学者、高校学生以及需要快速搭建后台管理界面的开发者使用覆盖了学校信息配置、用户登录注册、密码修改、学生档案增删改查与统计等典型业务场景。压缩包内共有246个文件以176个class编译文件与49个java源文件为主体同时包含sql数据库脚本、xml配置文件、png界面截图与chm帮助文档整体大小仅1.74MB目录层次简洁便于对照源码与编译产物进行学习和调试。项目基于Swing组件构建图形界面配合MySQL存储业务数据。目前已有1327人浏览学习该资源。系统实现了按学号、姓名、班级和系部四种查询方式支持按学号、班级、系部三维度删除记录并能按性别、班级、系部统计人数功能逻辑完整。由于附带了sql数据库脚本与帮助文档可直接导入MySQL数据库运行演示是理解Swing界面事件处理、DAO分层与MySQL交互流程的良好范本。1. 学生信息管理系统Java GUI MySQL 的课程设计源码第一眼该怎么看学生信息管理系统MySQL版这套 Java GUI 源码我拆包后的第一反应是它比网上大多数“课程设计源码”靠谱因为类名直接对应窗口功能还附带 chm 帮助文档不是那种解压后缺文件跑不动的半成品。它要解决的核心问题是刚学完 Java Swing JDBC 的人缺少一套能交作业、能二次开发的管理系统底子。登录注册、密码修改、学生记录增删改查、按学号姓名班级系部查询、按性别班级系部统计都是能跑通也能对着老师讲清楚的功能。适合 Java 课程设计、数据库实训也适合想快速搭一个 Swing 管理端再继续加功能的人。2. 拆解 rar从 MainFrame 到 StudentDaoImpl先摸清类分工再谈运行解压后第一眼看到的是一堆 .class 文件和帮助文档.chm第一次拿到的同学容易直接双击 MainFrame.class结果什么反应都没有。不要急先看类名分工再配运行环境。这一步理顺了后面改代码和调试都会快很多。2.1 类名即用途MainFrame 管主界面剩下几个 Frame 管具体业务窗口我把压缩包里的类按功能分了组对应关系很清楚类名职责MainFrame主窗口登录成功后的功能入口菜单和按钮都在这StudentDaoImpl数据访问实现所有增删改查 SQL 都集中在这个类里EditStudentFrame新增和修改学生记录共用的编辑窗口DeleteStudentsByDepartmentFrame按系部删除学生的确认窗口DeleteStudentsByClassFrame按班级删除学生DeleteStudentByIdFrame按学号删除单个学生FindStudentByIdFrame按学号查询FindStudentsByDepartmentFrame按系部查询结果多半是多条记录FindStudentsByClassFrame按班级查询FindStudentByIdFrame按学号精确查询从命名能看出这套代码没有把窗口逻辑和 SQL 揉在一起。MainFrame 负责跳转StudentDaoImpl 负责数据库操作各个 Find 和 Delete 窗口负责收集条件并调用 DAO。只要打开 StudentDaoImpl基本就看到全套 SQL 写法了比从界面一层层点进去快得多。有一点要注意压缩包里直接看到的是编译后的 .class。正常课程设计源码会附带 .java 源文件如果下载的包只有 .class学习时可以先用反编译工具或对照 chm 帮助文档还原逻辑但交作业前最好补齐源码。我的习惯是拿到任何这种资源包先 grep 一下有没有 .java没有就先列一个“反编译还原清单”免得改代码时无源可依。2.2 JDK、MySQL 与驱动 jar这个项目的最小运行组合这种老派 Swing JDBC 项目运行环境非常固定但也是最容易翻车的地方。我推荐的组合是JDK 8Swing API 最稳高版本 JDK 编译老代码偶尔会出现模块化报错MySQL 5.7 或 8.0都可以但驱动选择不一样数据库连接驱动mysql-connector-java版本和 MySQL 版本要匹配。如果用的是 Maven直接在 pom.xml 里加依赖dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency如果不熟悉 Maven就把这个 jar 下载后放到项目根目录的 lib 文件夹然后在 IDE 里右键 “Add as Library”。注意 MySQL 8.0 的驱动类名和 5.x 不一样后面避坑章会专门讲。这套源码跑起来后是桌面程序不是网页依赖的只有 Java 运行时和 MySQL 服务没有 Tomcat没有中间件。环境越小越适合课程设计这种场景答辩演示时不容易因为外部依赖出幺蛾子。2.3 帮助文档.chm 打不开时的替代方案chm 是 Windows 老的帮助文档格式双击打不开最常见的原因是“文件被标记为来自其他计算机”。解决方法是右键文件打开属性勾选“解除锁定”再双击就正常了。如果解除锁定后还是白屏可以用 7-Zip 直接打开 chm 文件它会显示里面的 html 页面列表把想要的内容拖出来就能看。这个技巧在 Windows 11 上尤其有用。chm 里通常写着数据库配置步骤、默认账号、运行方式这些源码包不一定会写在代码注释里的信息值得花十分钟过一遍。3. 建库与连接两张表的 SQL 设计以及一个让 JDBC 不乱码的连接串这套系统说白了就两个核心数据载体用户表和学生表。登录、改密码依赖用户表增删改查统计全部围绕学生表。表结构不复杂但字段命名、字符集、连接串参数都是后面能不能跑通的关键。很多同学代码从网上复制过来一执行就报错问题往往不在 Java而在 MySQL 这边。3.1 建表 SQL字段怎么定为什么学号必须设主键我按功能介绍里的字段重新整理了一份建表 SQL可以直接在 Navicat 或命令行执行CREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4; USE student_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, phone VARCHAR(20), register_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE t_student ( stu_id VARCHAR(20) PRIMARY KEY, name VARCHAR(50) NOT NULL, gender ENUM(男,女) DEFAULT 男, age INT, department VARCHAR(50), class_name VARCHAR(50), phone VARCHAR(20) );字段对应关系t_user 的 username 是登录用户名password 存的是注册时设置的密码register_time 记录注册时间t_student 里 stu_id 是学号department 是系部class_name 是班级。班级字段之所以不叫 class是因为 class 在 MySQL 里虽然是允许的但容易和关键字混淆加 _name 后缀更安全。两个细节建议照抄。第一学号必须设主键因为学号天然唯一用自增 id 反而会让“按学号删除、按学号查询”多一次关联。第二字符集用 utf8mb4不用 utf8utf8mb4 是 utf8 的超集能存生僻字和特殊符号避免后续导入数据时报“Incorrect string value”。性别字段选了 ENUM(男,女)简单直观适合课程设计。如果追求性能和扩展性可以用 TINYINT 加字典表但这套系统的定位是教学演示ENUM 足够答辩时也好解释。3.2 DBUtil 工具类连接串参数决定你是顺跑还是踩坑所有 DAO 类里都会用到数据库连接。我拆包后看这套系统的标准做法是写一个 DBUtil 工具类统一管连接和关闭。核心代码是这个样子public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/student_db ?useUnicodetruecharacterEncodingutf8 serverTimezoneAsia/ShanghaiuseSSLfalse; private static final String USER root; private static final String PASSWORD root123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(ResultSet rs, PreparedStatement ps, Connection conn) { if (rs ! null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (ps ! null) { try { ps.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn ! null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }连接串里的三个参数要背下来。useUnicodetruecharacterEncodingutf8是中文不乱码的关键少了它插入中文后 select 出来全是问号。serverTimezoneAsia/Shanghai是 MySQL 8.0 的强制要求5.7 不加也能跑8.0 不加直接报时区错误。useSSLfalse是为了关掉本地连接时 SSL 警告日志。驱动类名com.mysql.cj.jdbc.Driver也是 MySQL 8.0 的写法MySQL 5.7 时代用的是com.mysql.jdbc.Driver。如果驱动 jar 是 8.0 以上类名写老版本静态块里就不会执行后面 getConnection 就会报 ClassNotFoundException。这是这个项目最常见的启动失败原因没有之一。3.3 注册时间别在 Java 里 new Date交给数据库默认值再看注册功能。t_user 表里 register_time 字段我写了DEFAULT CURRENT_TIMESTAMP意思是插入时不传这个字段MySQL 会自动写入当前时间。这样做的好处是避免 Java 和 MySQL 之间的时区差异也减少代码量。对应的插入 SQL 长这样String sql INSERT INTO t_user(username, password, phone, register_time) VALUES(?, ?, ?, NOW());用NOW()而不是在 Java 中new java.util.Date()再格式化是这类课程设计源码里的通用做法。因为 SimpleDateFormat 的格式串、时区、线程安全都是额外要讲的点直接在 SQL 层解决最省事答辩时也能说成“让数据库统一管理时间来源”反而是一个加分回答。4. 登录、增删改查与统计源码里值得抄的 DAO 写法和 SQL 参数功能层面的代码拆包后主要看 StudentDaoImpl 和各个 Frame 的调用关系。下面这些写法不一定是原版源码每个字都一样但在这个项目的场景下是合格从业者最可能采用的实现套路。照着抄能改能跑也能讲清楚。4.1 登录校验用 PreparedStatement 占位符别用字符串拼接登录功能在 t_user 表上做校验核心逻辑是根据用户名查密码比对通过返回 true。用 PreparedStatement 的好处是防 SQL 注入这也是老师最喜欢问的一个点。public boolean checkLogin(String username, String password) throws SQLException { String sql SELECT COUNT(*) FROM t_user WHERE username? AND password?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { rs.next(); return rs.getInt(1) 0; } } }COUNT(*)返回 0 或 1大于 0 表示用户名和密码匹配。两个问号是占位符通过 setString 绑定参数数据库会把它们当字符串值处理而不是当作 SQL 语句的一部分。不要在代码里写成SELECT ... WHERE username username 这等于把用户名拼进 SQL遇到特殊字符会直接报语法错误甚至被注入。登录窗口拿到 true 后创建 MainFrame拿到 false 弹错误提示框。密码修改功能也走类似的套路先验证旧密码再 update 新密码这两步最好放在同一个 DAO 方法里减少窗口和 DAO 之间的来回交互。4.2 新增和修改共用一个窗口isEdit 标志切换 SQL少写一半代码EditStudentFrame 同时管新增和修改常见做法是用一个布尔变量区分public boolean saveStudent(Student s, boolean isEdit) throws SQLException { String sql; if (isEdit) { sql UPDATE t_student SET name?, gender?, age?, department?, class_name?, phone? WHERE stu_id?; } else { sql INSERT INTO t_student(stu_id, name, gender, age, department, class_name, phone) VALUES(?,?,?,?,?,?,?); } try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, s.getName()); ps.setString(2, s.getGender()); ps.setInt(3, s.getAge()); ps.setString(4, s.getDepartment()); ps.setString(5, s.getClassName()); ps.setString(6, s.getPhone()); if (isEdit) { ps.setString(7, s.getStuId()); } return ps.executeUpdate() 0; } }这个设计的关键在于新增时学号出现在 INSERT 的第一个值修改时学号出现在 UPDATE 的 WHERE 条件里参数位置不一样所以 bind 参数时要用 if 分支调整顺序。源码里 Judge 一个布尔值窗口标题切换成“新增学生”或“修改学生”表单输入框回显已有数据。明白这个套路后再去看 EditStudentFrame 里那些“为什么有的框能编辑有的不能编辑”的逻辑就通了——edit 模式下学号框通常会被 setEditable(false)防止改主键。executeUpdate()返回受影响的行数大于 0 表示操作成功。这个方法在新增、修改、删除三个功能里通用建议背下来。4.3 三种删除和四种查询传参方式决定代码复用度删除功能分按学号、按班级、按系部三种。这三种的 DAO 写法结构一样只是 SQL 条件和绑定参数不同我这里以按系部删除为例public int deleteByDepartment(String department) throws SQLException { String sql DELETE FROM t_student WHERE department?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, department); return ps.executeUpdate(); } }按学号删除就是WHERE stu_id?按班级删除就是WHERE class_name?。班级和系部删除可能一次删多行executeUpdate 返回的 int 值可以显示“本次删除 N 条记录”。在删除窗口里建议先根据条件查一次把将受影响的学生列出来给用户确认再执行删除后面避坑章我单独讲。查询功能也是同样的套路。按学号查询用精确匹配WHERE stu_id?结果集最多一条按姓名、系部、班级查询如果希望支持模糊搜索SQL 就写成String sql SELECT * FROM t_student WHERE name LIKE ?; ps.setString(1, % keyword %);注意%要加在参数里不是加在 SQL 语句里。这样 PreparedStatement 依然把整体当参数% 是普通字符不会有注入风险。查询结果遍历 ResultSet每行封装成一个 Student 对象放到 List 里返回再在 JTable 里展示。如果查询条件是学号返回范围可以缩窄为一个对象直接弹窗显示或回填表单。4.4 统计结果展示GROUP BY 之后怎么填进 JTable统计功能按性别、班级、系部统计人数底层就是一个聚合查询。按系部统计的 DAO 方法长这样public MapString, Integer countByDepartment() throws SQLException { MapString, Integer map new HashMap(); String sql SELECT department, COUNT(*) FROM t_student GROUP BY department; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { map.put(rs.getString(1), rs.getInt(2)); } } return map; }这里两个细节值得注意。第一ResultSet 读取列时可以用列索引rs.getString(1)取第一列 departmentrs.getInt(2)取第二列 COUNT(*) 的结果。第二统计结果不经过 Student 对象直接用 Map 收集表格展示时遍历 Map 转成二维数组填进 JTable。按性别统计就是GROUP BY gender按班级统计就是GROUP BY class_name改一下 GROUP BY 字段同一个 DAO 方法可以复用三次。JTable 展示统计结果时表头数组要和 Map 的 key 对应。比如表头第一列叫“系部”第二列叫“人数”遍历 Map 时取 entry.getKey() 和 entry.getValue() 拼行数据。麻烦点在于 Map 是无序的如果希望系部按字母排可以在 SQL 里加ORDER BY department。5. 避坑与常见问题驱动类名、端口、乱码和“双击 class 没反应”的四个翻车现场这套源码我实际复现时踩过的坑基本集中在环境配置而非 Java 语法上。下面四条是按出现频率排的每一条都是“现象→原因→解决”的结构直接对着排查。5.1 Class.forName 报 ClassNotFoundException驱动版本和类名不匹配现象运行 MainFrame 或任意 DAO 方法控制台第一行就是java.lang.ClassNotFoundException: com.mysql.jdbc.Driver。原因mysql-connector-java 8.0 版本把驱动类从com.mysql.jdbc.Driver改成了com.mysql.cj.jdbc.Driver而网上很多老教程和源码里写的是旧类名。驱动 jar 版本和代码里的类名不一致静态块里的 Class.forName 就抛异常。解决第一步看项目里 jar 包版本。如果是 8.0.x把 DBUtil 里的驱动类名改成com.mysql.cj.jdbc.Driver。如果是 5.1.x保持旧类名同时在连接串里去掉serverTimezone参数因为 5.x 不认识这个参数。换驱动版本和换类名必须同步做这是不少同学改一处漏一处的坑。5.2 Cant connect to MySQL server on localhost服务没启动端口被占用现象java.sql.SQLException: Communications link failure或者Cant connect to MySQL server on localhost (10061)。原因MySQL 服务没启动。在 Windows 上MySQL 安装后默认不是开机自启很多课程设计机的 MySQL 服务是手动启动的。另一种情况是本机装了多个 MySQL 版本8080 端口被占用连接串还指向旧端口。解决Windows 下按 WinR 输入services.msc找到 MySQL 服务状态如果是“停止”点启动并把启动类型改成自动。端口问题用命令行netstat -ano | findstr 3306查看 3306 是否被其他进程占用被占用就改 MySQL 的 my.ini 端口同时改连接串里的 URL。Linux 环境下经常遇到ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock这个大概率是服务没起来或者 socket 文件路径不对用systemctl status mysql先确认服务状态。5.3 中文存进去变成问号连接串缺编码库字符集不对现象在窗口里输入“张三”插入数据库后显示“???”或者 chm 文档里的说明数据导入后乱码。原因连接串里没有useUnicodetruecharacterEncodingutf8或者建库时用了默认的 latin1 字符集。两处只要有一处不对中文就会出问题而且查出来的数据已经乱了代码层面救不回来。解决第一建库 SQL 显式指定字符集用本文第 3 章的DEFAULT CHARACTER SET utf8mb4第二DBUtil 的连接串必须带编码参数第三如果数据已经写乱了先删掉表数据修改连接串和库字符集后重新插入。推荐在改动前先执行SHOW CREATE TABLE t_student;看表当前的字符集再决定是 ALTER TABLE 改字符集还是直接 drop 重建。5.4 双击 .class 没有任何反应class 文件本来就不是这么跑的现象解压后在文件夹里双击 MainFrame.classWindows 弹窗让选择打开方式或者干脆没反应。原因.class 是 Java 字节码需要 JVM 解释执行不能像 exe 一样双击运行。这是新手最容易误解的地方。解决正确姿势是在源码目录打开命令行先编译再运行。假设项目根目录下放着 MainFrame.class 和 mysql-connector-java-8.0.33.jarlib 里是驱动包命令是java -cp .;lib/mysql-connector-java-8.0.33.jar MainFrameWindows 用分号;分隔 classpathLinux 和 macOS 用冒号:。这条命令的意思是告诉 JVM当前目录和 lib 目录下的 jar 里都有类入口类是 MainFrame。如果没有 .java 源文件只有 .class只要 classpath 配置正确一样能运行这点倒是 .class 比 .java 好使的地方。5.5 JTable 中文乱码和表头显示成小方块界面字体没设置现象数据库里的中文正常但 JTable 表头显示乱码或者按钮文字挤成一团。原因Swing 默认字体在中文 Windows 上应该没问题但如果系统设置了非默认字体渲染JTable 表头会显得特别小有的 Linux 环境下没有中文字体直接显示方块。解决在 MainFrame 的初始化代码里给 JTable 和表头设置字体常见做法是JTable table new JTable(model); table.getTableHeader().setFont(new Font(微软雅黑, Font.PLAIN, 13)); table.setFont(new Font(微软雅黑, Font.PLAIN, 13));Linux 下没有微软雅黑改成new Font(Noto Sans CJK SC, Font.PLAIN, 13)或者Dialog字体。这条属于“低频但出现一次就让你焦头烂额”的坑答辩演示时表头乱码非常尴尬提前设字体最省心。6. 二开时必做的三处改造学号查重、删除确认与密码摘要存储源码能跑通只是第一步。交作业或自己留着做底子时我建议优先做三处改动成本低讲得出亮点还能把这套系统的健壮性往上拉一个档次。6.1 新增学生前查重用唯一索引和 DAO 双重兜底数据库层面已经把 stu_id 设成主键重复插入会报异常但异常提示对用户不友好。更稳妥的做法是在 EditStudentFrame 保存前先调一次查询public boolean existsById(String stuId) throws SQLException { String sql SELECT COUNT(*) FROM t_student WHERE stu_id?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, stuId); try (ResultSet rs ps.executeQuery()) { rs.next(); return rs.getInt(1) 0; } } }在新增模式下如果 existsById 返回 true直接弹窗“学号已存在”不执行 insert。这样既避免报错也减少一次无意义的 SQL 执行。数据库唯一索引是兜底DAO 查询是体验两层都做才算完整。6.2 删除前先统计受影响记录把“查询删除”放进同一个事务按系部或班级删除时最怕用户手一抖删掉几百条。我的习惯是删除前先查一次受影响的学生数量展示确认对话框确认后再执行 delete。如果想让这两个操作在逻辑上保持一致可以用事务包裹Connection conn DBUtil.getConnection(); try { conn.setAutoCommit(false); int count countByDepartment(conn, department); if (count 0) { int deleted deleteByDepartment(conn, department); // 比较 deleted 和 count不一致说明有并发插入 } conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); }单表操作的场景下事务显得有点重但这是练习setAutoCommit(false)和rollback()的最佳时机。把事务写法留在这套系统里下次做多表级联删除时就能直接复用这也算给自己留一份后悔药。6.3 密码摘要存储MD5 摘要比较代替明文t_user 表里如果直接存明文密码答辩时老师一问“密码安全怎么考虑”就露怯了。改成 MD5 摘要存储只需要几十行代码public static String md5(String password) throws Exception { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(password.getBytes(StandardCharsets.UTF_8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); }注册时调用md5(rawPassword)再存入数据库登录时先对输入做同样的摘要再和库里比对。改动点只有两个 DAO 方法却能挡住直接查库看密码的问题。课程设计做到这个程度已经超过百分之八十的同题作品了。从那以后我每次接手这种 Java GUI MySQL 的课程设计源码都会强制先走一遍“建库、改连接串、跑登录”的流程再谈改功能。环境问题看着琐碎实际上比业务逻辑更消耗耐心。这套系统的类分工和 DAO 分层是它最大的价值把这三处改造加上去它就能从“能交作业”变成“能拿出去讲”的作品。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站