ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Java+MySQL学生管理系统:从Swing界面到JDBC数据访问的完整实践

Java+MySQL学生管理系统:从Swing界面到JDBC数据访问的完整实践 简介面向Java初学者与课程设计学生提供一套基于Java控制台与MySQL的学生信息管理系统完整学习资料。项目为V3.0单管理员版本以控制台打印方式交互管理员登录后支持学生信息的添加、删除、修改、查询运行于Eclipse/MyEclipse可直观理解Java SE与MySQL数据库配合开发的流程。压缩包共33个文件总体积约37.67MB按用途整合了Java源码及Class编译文件、MySQL建库建表SQL脚本、连接数据库所需Jar驱动包、记录各功能界面的PNG截图、讲解设计思路的DOCX技术说明书、演示完整操作的MP4视频以及运行必读文本目录结构清晰便于按模块快速定位到对应代码或文档。已有66人学习下载适合作为课程设计参考或Java基础巩固的实用练手项目。读者可依照运行必读快速搭建环境通过技术说明书把握系统设计脉络结合演示视频对照源码领会控制台输入输出、学生信息管理业务、JDBC访问数据库及基本异常处理等关键环节进而在此基础上扩展新的功能模块或改造界面提升实际编码能力与项目经验。1. 学生信息管理系统Java、控件台与 MySQL 这一组合在解决什么问题基于 Java、控件台Swing 控件集和 MySQL 的学生信息管理系统是课设和毕设里出现频率最高的题目之一也是把 Java 基础语法、面向对象封装、JDBC 数据库操作和 SQL 设计一次串起来的最短路径。控件台指的就是 JFrame、JTextField、JTable 这一套 Java 图形控件MySQL 负责存学生档案、班级和账号数据演示视频则承担着把系统“讲清楚”的交付任务。很多人误以为这套组合过时但面试会被追问的 JDBC 预编译、事务控制、字符集问题全在这个项目里有真实落点。它适合正在做课设、或想补 Java 数据库基础的开发者照着从设计到落地的顺序把系统做出来。2. 先设计再写码模块划分与 MySQL 表结构字段与建表 SQL2.1 别急着写类控件台项目最常见的分层方式做这个题最容易翻车的做法是打开 IDEA 就写一个 Student 类把界面、SQL、业务判断全堆在里面。能跑但答辩问一句“改需求怎么办”就卡壳。常见做法是分成四层实体层modelStudent、User只放属性和 getter/setter。数据访问层dao只写 SQL 的查询、插入、更新、删除返回结果或受影响行数。业务层service参数校验、业务规则判断调用 dao。界面层ui用 Swing 控件搭窗体收集输入、调用 service、刷新表格。控件台项目的界面层核心是 JFrame 这个顶层容器里面再叠 JPanel、JLabel、JTextField、JTable 这些控件。JFrame 本身是个容器所有控件都往它上面挂JTable 是展示学生列表最合适的选择它自带滚动条和列头不用自己画表格。这一层的职责是数据展示和输入采集不该出现 SQL 字符串。包结构按层建类多了以后不会乱com.student.model、com.student.dao、com.student.service、com.student.ui。依赖方向是 ui 依赖 serviceservice 依赖 daodao 只是对着数据库连接操作。这个分层不是教条它解决的是实际维护问题界面要加一个按钮不需要动 SQLSQL 要加一个筛选条件不需要动界面。对一个信息管理系统来说这种隔离让改需求变成十分钟的事而不是重写一个类。2.2 建表 SQL学生表、用户表和班级表的字段取舍学生信息管理系统的核心数据是学生档案最少需要两张表t_user 管登录t_student 管学生。加一张 t_class 可以让学生表只存 class_id不重复存班级名字。下面是这份设计的建表脚本CREATE DATABASE IF NOT EXISTS stu_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE stu_db; CREATE TABLE t_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, role TINYINT DEFAULT 0 COMMENT 0-管理员 1-普通用户 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_class ( id INT AUTO_INCREMENT PRIMARY KEY, class_name VARCHAR(50) NOT NULL, grade VARCHAR(20) DEFAULT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_student ( id INT AUTO_INCREMENT PRIMARY KEY, sno VARCHAR(20) NOT NULL UNIQUE COMMENT 学号业务唯一键, name VARCHAR(50) NOT NULL, gender TINYINT DEFAULT 0 COMMENT 0-女 1-男, age INT DEFAULT NULL, class_id INT DEFAULT NULL, major VARCHAR(50) DEFAULT NULL, enroll_date DATE DEFAULT NULL, del_flag TINYINT DEFAULT 0 COMMENT 0-在册 1-已删除, create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_stu_class FOREIGN KEY (class_id) REFERENCES t_class(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;字段取舍上有几个关键点第一次做的人很容易踩。sno 学号设置成 UNIQUE 而不是主键因为学号有业务含义拿它当主键将来改学号时所有关联数据都要跟着动用自增 id 做主键学号用唯一索引约束这是更稳的做法。gender 用 TINYINT 而不是 CHAR(2)存 0/1 比存“男/女”省空间界面层再翻译成中文显示。del_flag 是逻辑删除字段学生退学不物理删行只把 del_flag 置 1数据可追溯后面做“删除”功能时这一条会省很多事。存储引擎统一用 InnoDB不选 MyISAM。原因很简单后面写事务、批量插入成绩时只有 InnoDB 支持MyISAM 在事务上没有任何后悔药。字符集选 utf8mb4 不是为了存 emoji而是 utf8mb4 覆盖了真正的四字节字符生僻字和特殊符号不会写入报错或变乱码MySQL 5.7 和 8.0 都认这个写法。建库语句里 DEFAULT CHARACTER SET 和 COLLATE 一起指定避免库、表、连接三级字符集不一致。2.3 数据访问层的 SQL 设计查询、排序、去重这套系统的查询基本盘是“按姓名模糊查”和“按学号精确查”。模糊查询用 LIKE 加百分号但百分号拼在哪一层有讲究-- 推荐参数里带通配符 SELECT * FROM t_student WHERE name LIKE ? AND del_flag 0 ORDER BY sno; -- 避免SQL 字符串里拼接容易拼出语法错误或注入问题 -- SELECT * FROM t_student WHERE name LIKE % || ? || %;ORDER BY sno 让列表按学号排序。排序这个动作交给 MySQL 走索引而不是把数据全部取回 Java 里用 Collections.sort 再排。数据量几百行时两种写法都能跑但把 java 排序和 SQL 排序的职责分开习惯养成后面对几万行数据不会返工。查询逻辑里要习惯性带上 del_flag 0防止“删除”之后列表里还看得到。这是我第一版忘写的条件后来被测试数据直接打脸。另一个容易被忽略的点是空字符串搜索keyword 传空值时代码里要做一次判断直接返回空列表而不是把整张表查出来再在 Java 层面过滤。这种边界虽然小但界面层每次输入框一变空就查全表体验会明显变差。3. 把环境跑通JDK、MySQL 和 JDBC 驱动的版本匹配与最小配置3.1 JDK 环境变量配置与 MySQL 5.7 / 8.0 的选择运行环境先交代清楚。JDK 用 8 或 11 都行Swing 从 JDK 8 开始就是标准库不需要额外集成。环境变量上只需要配 JAVA_HOME 和 Path。很多老教程让你配 CLASSPATHJDK 9 之后编译器和虚拟机不再依赖 CLASSPATH 指定的路径来加载标准库配了反而可能覆盖默认类路径造成 ClassNotFoundException。CLASSPATH 我只在面试题里解释它的历史实操中不会动它。MySQL 版本选 5.7 还是 8.0是第一个影响代码的决策也是卡住很多人的第一道坎。MySQL 5.7 是老教程的默认环境驱动类名用 com.mysql.jdbc.DriverMySQL 8.0 的驱动类名改成了 com.mysql.cj.jdbc.Driver并且默认开启 SSL 和 sha2_password 插件连接 URL 必须多带 serverTimezone 和 useSSL 参数否则连上去立刻报错。如果你跟着 mysql 安装教程 8.0 装完却复制了 5.7 的代码启动时必翻车。Windows 下我装 MySQL 习惯用解压版不太点那个 msi 安装包。官网下载 ZIP 包解压到 D:\mysql-8.x以管理员身份打开终端在 bin 目录下执行mysqld --initialize-insecure mysqld --install MySQL80 net start MySQL80--initialize-insecure 会生成一个无密码的 root 账户本地学习足够正式环境不要这样用后续要马上设置强密码。--install 把 MySQL 注册成 Windows 服务net start 启动它。装过旧版 MySQL 的机器上先停掉旧服务再用 netstat 确认 3306 没有占用否则服务起来了程序也连不上。这一套检查逻辑就是 mysql 安装配置教程里最容易跳过的部分也是你以后排查环境问题的主线。3.2 JDBC 驱动与连接 URL 的参数细节驱动是一个 jar 包。非 Maven 项目把 mysql-connector-j 的 jar 放进项目根目录的 lib 文件夹右键 Add as LibraryMaven 项目在 pom.xml 里加依赖版本号和你装的 MySQL 大版本对应。不要下载一个驱动就丢进去不管版本Connector/J 5.x 和 8.x 的驱动类名、默认参数都不一样混用会直接报类找不到。连接参数是我建议在项目一开始就固化下来的内容后面所有类都从这里受益参数作用建议值useSSL是否使用 SSL 连接false本地开发serverTimezone服务器时区Asia/ShanghaicharacterEncoding客户端字符集utf8mb4 或 utf8allowPublicKeyRetrieval允许获取 sha2 公钥true本地开发第一次做的时候我连写 jdbc:mysql://localhost:3306/stu_db 都连不上去报 SSL connection error就是漏了 useSSL 参数。URL 里时区写成 GMT%2B8 是另一种写法但不如 serverTimezoneAsia/Shanghai 直观。characterEncoding 不指定MySQL 客户端默认用 latin1中文写到库里全变问号。这四个参数本地开发按表里给的填代码里统一放常量别散落得到处都是。3.3 用 Navicat 或命令行验证 MySQL 连接环境搭好后先不急着写 Java用命令行或 Navicat for MySQL 测一下数据库连通性。Navicat 连接时主机填 localhost端口 3306用户名 root密码填你设置的点测试连接。测试通过后新建数据库 stu_db把第 2 章的建表 SQL 粘到查询窗口执行。这一步能验证三件事MySQL 服务在跑、root 密码没记错、建表语句没有语法错误。如果 Navicat 连不上而命令行能连多半是 root 的 host 限制只允许 localhost这时新建一个专门给程序用的账号更省事也顺带把 root 密码和代码隔离CREATE USER stu_applocalhost IDENTIFIED BY stu_pass; GRANT ALL PRIVILEGES ON stu_db.* TO stu_applocalhost; FLUSH PRIVILEGES;程序里用 stu_app 而不是 root密码独立管理这是数据库权限最小化的基本要求。项目交付给别人的时候这套配置也要一并写清楚否则对方拿到代码连不上库第一反应是代码有问题实际是环境没对齐。4. 核心功能落地从登录模块到学生信息增删改查的 Java 代码4.1 写一个所有类都能复用的数据库连接工具类DAO 里每个方法都要拿 Connection所以连接工具类放在 com.student.util 包下用静态方法对外提供。参数表前面已经定好了类里直接拼出来package com.student.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DbUtil { private static final String DRIVER com.mysql.cj.jdbc.Driver; private static final String URL jdbc:mysql://localhost:3306/stu_db ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; private static final String USER stu_app; private static final String PASSWORD stu_pass; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { // 驱动 jar 没引入或类名写错时启动就能暴露不用等到连库 throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }Class.forName 的作用是让驱动类在 JVM 里完成注册新版本驱动虽然可以自动注册但保留这行代码能兼容老环境。放在静态块里类一加载就执行驱动缺失会第一时间报出来而不是等运行到登录模块才冒出 ClassNotFoundException。URL 里的三个参数缺一不可少 serverTimezone 报时区异常少 useSSL 在 MySQL 8.0 上报 SSL 错误少 characterEncoding 收获一表乱码。这三个参数是踩坑高频区建议直接背下来。4.2 登录模块用 PreparedStatement 挡住 SQL 注入登录是系统的入口最容易写错的地方不是界面布局而是 SQL 拼接。新手写法是拼字符串// 反面写法String sql SELECT * FROM t_user WHERE username name AND password pwd ;这种写法在面试里是要被考死的也是项目里最不能被接受的安全漏洞。正确做法是占位符配合 PreparedStatementpublic User login(String username, String password) { String sql SELECT id, username, role 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()) { if (rs.next()) { User u new User(); u.setId(rs.getInt(id)); u.setUsername(rs.getString(username)); u.setRole(rs.getInt(role)); return u; } } } catch (SQLException e) { e.printStackTrace(); } return null; }PreparedStatement 会把参数值交给驱动做转义用户输入里的单引号、分号都没法破坏 SQL 结构登录这种公共入口这是底线。try-with-resources 会让 Connection、PreparedStatement、ResultSet 自动关闭省掉手动 finally 关闭又怕关错顺序的麻烦。注意参数索引从 1 开始不是 0ResultSet 取列按下标从 1 开始按列名取可读性更好列名写错也会立即报错而不是静默取错值。4.3 学生管理增删改查新增、模糊查询与列表展示学生模块的四件事统一放在 StudentDao 里。新增的 INSERT 语句字段多对应 prepareStatement 里 7 个占位符public int addStudent(Student s) throws SQLException { String sql INSERT INTO t_student (sno, name, gender, age, class_id, major, enroll_date) VALUES (?,?,?,?,?,?,?); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, s.getSno()); ps.setString(2, s.getName()); ps.setInt(3, s.getGender()); ps.setInt(4, s.getAge()); ps.setInt(5, s.getClassId()); ps.setString(6, s.getMajor()); if (s.getEnrollDate() ! null) { ps.setDate(7, new java.sql.Date(s.getEnrollDate().getTime())); } else { ps.setNull(7, Types.DATE); } return ps.executeUpdate(); } }executeUpdate 返回受影响行数等于 1 说明执行成功界面层据此弹提示。enroll_date 可能为空用 setNull 而不是把空字符串硬塞进去否则数据库层面会报日期格式错误。这是新增功能里最容易忽略的边界尤其是界面上日期选择框允许清空的时候。模糊查询和列表展示是一对DAO 返回 ListJTable 拿数据转表格模型public ListStudent findByName(String keyword) throws SQLException { String sql SELECT * FROM t_student WHERE del_flag 0 AND name LIKE ? ORDER BY sno; ListStudent list new ArrayList(); try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % keyword %); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Student s new Student(); s.setId(rs.getInt(id)); s.setSno(rs.getString(sno)); s.setName(rs.getString(name)); s.setAge(rs.getInt(age)); s.setMajor(rs.getString(major)); list.add(s); } } } return list; }百分号写在参数里不在 SQL 字符串里拼接这样 keyword 为空时结果也安全。ORDER BY sno 把排序交给数据库Java 侧不用再调排序接口。JTable 这边用 DefaultTableModel 装载列表刷新时先清空行再重新 addRow这是 Swing 列表刷新的固定套路不要直接给 JTable setModel 一个新模型那样表头配置会跟着丢。修改和删除的方法与新增同一个套路写 SQL、占位符、executeUpdate、判断返回行数。删除其实不执行 DELETE而是 UPDATE t_student SET del_flag 1 WHERE id ?这就是建表时留 del_flag 字段的原因。这一套路子吃透学生模块的核心代码就齐了下面来看最容易让演示现场崩掉的几个坑。5. 避坑排查学生管理系统最常见的五个翻车现场5.1 MySQL 8.0 连接报 SSL 错误现象main 方法一执行就抛 com.mysql.cj.exceptions.CJCommunicationsException控制台提示 SSL connection error或者干脆 Communications link failure。这不是代码逻辑错是连接 URL 缺少协商参数客户端和服务端在 SSL 握手阶段没有达成一致。原因MySQL 8.0 默认开启 SSL 能力客户端没指定 useSSL 时双方会尝试协商失败就断开同时 8.0 的 sha2_password 插件在非 SSL 连接下要求 RSA 公钥交换缺 allowPublicKeyRetrievalTrue 也会直接断。解决URL 统一补三段参数useSSLfalse、serverTimezoneAsia/Shanghai、allowPublicKeyRetrievaltrue。本地开发这样配没有问题部署到服务器再按安全要求重新评估 SSL 策略。如果你搜 mysql ssl 连接错误解法基本都落在这三行参数上。5.2 数据库中文乱码现象界面查询出来学生姓名显示成问号库里直接存的是乱码控制台 print 中文也看不到正常内容。原因乱码是三层不一致的结果。建库字符集、连接参数 characterEncoding、Java 源码编码任何一环不是 utf8mb4/utf8 就会出问题。最常见的是建库时没指定字符集MySQL 默认落到 latin1G 到库里就没救了。解决建库语句带 DEFAULT CHARSETutf8mb4连接 URL 带 characterEncodingutf8代码文件统一 UTF-8IDEA 右下角显示编码位置检查一遍。改完后把表 DROP 重建重新导数据只改连接参数救不了已经存进去的坏数据。这是我做过最不值得的二次返工一次性配好能省两小时。5.3 ClassNotFoundException: com.mysql.jdbc.Driver现象启动直接抛 java.lang.ClassNotFoundException: com.mysql.jdbc.Driver程序还没走到连库就断了。原因两种可能。要么 jar 包根本没被加载进构建路径要么驱动类名按 5.7 写法用了 com.mysql.jdbc.Driver而你引的 jar 是 Connector/J 8.x。8.x 的驱动类名是 com.mysql.cj.jdbc.Driver写旧名找不到类。解决先看项目里有没有 jar 且被 Add as Library而不是只放在磁盘目录里再核对类名和 jar 版本是否对应。还有一个隐藏坑IDEA 里本地能跑导出 Runnable JAR 时没选把依赖打进包里换机器演示就继续报同样错误。导出时选包含依赖项的 fat jar导完先在本机双击跑一遍再交付。5.4 批量操作写到一半数据不一致现象新增一个学生时同时要写一条初始化成绩记录成绩表插入失败学生数据却已经留在库里。页面弹了错误库里却多了半截数据。原因两个 INSERT 各自自动提交第二个失败不影响第一个。这已经不是功能缺陷是数据一致性问题也是 Java 面试题里“怎么保证数据一致性”的现场版本。解决连接上手动打开事务把两步操作放进同一个事务边界里Connection conn DbUtil.getConnection(); try { conn.setAutoCommit(false); studentDao.addStudent(conn, s); scoreDao.initScore(conn, s.getId()); conn.commit(); } catch (SQLException e) { conn.rollback(); throw e; } finally { conn.setAutoCommit(true); conn.close(); }注意这两个 DAO 方法不再自己拿连接而是接受外部传入的 Connection——同一个连接才能组成同一个事务。如果 DAO 内部各自 new 连接事务无从谈起。MySQL 事务处理的基础就是这一句事务建立在同一个连接上跨连接的多个操作永远不可能是一个事务。5.5 MySQL 服务没启动或端口被占用现象报 Communications link failure 或 Connection timed out代码看半天没问题最后发现 MySQL 服务压根没跑。原因机器上装过多个 MySQL 版本服务名不是默认的 MySQL80或者旧服务占着 3306新装的改去了 3307程序却写死 3306。这类问题在接手别人电脑、或从旧机器迁移项目时特别常见。解决先看服务状态Windows 服务管理器里确认 MySQL80 是“正在运行”再看端口命令 netstat -ano | findstr 3306 查占用。查问题按从下往上的顺序走网络、服务、权限、SQL不要一上来就怀疑驱动版本。顺手把数据库连接四要素抽到 properties 配置文件里换环境只改配置不改代码这套做法到后面的项目也适用。6. 让系统真正能讲分页查询与演示视频里的验证清单6.1 分页查询给 JTable 加“上一页/下一页”学生列表超过几十条后一次性全查出来会让 JTable 渲染明显变慢分页是必须做的。MySQL 分页一句话LIMIT 偏移量, 每页条数。SELECT * FROM t_student WHERE del_flag 0 ORDER BY sno LIMIT ?, ?;第一个占位符是 (pageNum - 1) * pageSize第二个是 pageSize。DAO 加一个 count 方法查总数算出总页数Swing 界面上放两个 JButton 触发重新查询并刷新表格模型。分页不是为了炫技它让你的代码在数据量增长时不用返工同时传递出一个信息你考虑到了系统在真实使用下的表现。6.2 演示视频的验证清单题目里有“含演示视频”四个字这个视频的价值不是把项目跑一遍而是把系统边界讲清楚。我一般录之前按“登录 → 查询 → 新增 → 模糊搜索 → 修改 → 删除 → 权限演示”的顺序完整走一遍删除功能特意演示成“列表消失但库里还查得到”比直接物理删更能体现 del_flag 字段的设计意图。视频里主动制造一个出错场景输入重复学号系统提示“学号已存在”而不是让数据库报错弹到界面。这个细节比一路顺滑更有说服力评审能看出异常被拦在了业务层。Swing 默认字体小录屏前把窗体字号调大不然导出的视频全是模糊的小字。有余力的话把“按专业统计人数”的统计 SQL 写成 MySQL 存储过程在视频里单独讲一分钟同一个题目里这就是明显的加分动作。我第一次做这类系统SSL 参数、中文乱码、事务半截全撞了一遍答辩演示时还因为 MySQL 服务没启动卡在登录页。之后我把连接参数、字符集、事务边界这些整理成检查清单再做类似题目基本一次过。这套清单如果你能在自己项目里维护起来比背一百道 java 面试题管用。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进