☰
Java+MySQL学生信息管理系统:JDBC增删改查与Swing界面实战
2026/9/30 9:19:25 网站建设 项目流程

简介:这份资源面向Java初学者与需要完成课程设计的学生,提供一套基于Java Swing与MySQL的学生信息管理系统实现方案,重点解决使用JDBC对学生数据做增删改查的入门实践问题。压缩包内共1个PDF文件,约135KB,以图文形式呈现项目结构、数据库建表语句与核心代码片段,便于对照理解model-dao-view三层划分。内容涵盖管理员与学生两类实体建模、admin与student表的字段设计,以及登录、信息维护等基础功能的实现思路,适合作为课设参考或自学练手素材。目前已有5010人学习下载,说明其在同类入门项目中具备一定参考价值。读者可从中获取数据库脚本、实体类定义与分层组织方式,快速搭建可运行的管理系统雏形,并据此扩展查询、统计等模块。

1. Java+MySQL 学生信息管理系统:从课程设计到能跑通的增删改查

课程设计里十个有八个是学生信息管理系统,但真正能拿得出手、经得起答辩追问的不到三成。大部分项目卡在同一个地方:界面能点,数据库连不上;或者连上了,一改数据就报 SQLException。这个标题背后其实是一条完整的 Java 桌面开发链路——Swing 做界面、JDBC 做数据通道、MySQL 做持久化,三者缺一不可。它适合正在做课程设计的学生,也适合想用一个小项目把 JDBC 增删改查、事务、连接池串起来的 Java 初学者。我见过太多人把时间花在拖控件上,结果 JDBC 那层写得千疮百孔,换个环境就翻车。这篇笔记就按我实际做项目的顺序,把选型、建表、写 DAO、调参数、排错一条线讲清楚,让你做出来的东西不只是能演示,而是能讲明白每一行代码为什么这么写。

2. 环境搭建与数据库设计:先把地基打牢

2.1 JDK、MySQL 与 IDEA 的版本选择

动手之前先把工具链定下来,不然后面全是玄学问题。JDK 选 17 或 21 都行,LTS 版本稳定,别用太老的 8,Swing 在高版本 JDK 上对 HiDPI 支持更好。MySQL 用 8.0 系列,安装时记住 root 密码,字符集选 utf8mb4,排序规则用 utf8mb4_0900_ai_ci。IDEA 社区版就够,创建项目时选 Java 项目,不要选 Maven 骨架,课程设计阶段手动加 jar 包更直观。

MySQL 安装完先验证服务能不能连上。Windows 下用管理员权限开命令行,执行:

mysql -u root -p

输入密码后如果进了mysql>提示符,说明服务正常。如果报ERROR 2002 (HY000): Can't connect to local MySQL server through socket,先检查 MySQL 服务有没有启动,Windows 在服务管理器里找 MySQL80,Linux 用systemctl status mysql。这个报错九成是服务没起来,不是配置问题。

提示:安装 MySQL 时如果选了默认端口 3306,后面 JDBC URL 里就写 3306;如果改过端口,URL 里必须同步改,否则连接直接被拒。

2.2 学生信息管理系统的表结构设计

表设计决定了后面 DAO 层好不好写。学生信息管理系统核心就一张学生表,但为了演示关联查询,我一般再加一张班级表。学生表字段包括学号、姓名、性别、年龄、班级 ID、入学日期、联系电话。学号做主键,用 VARCHAR 而不是 INT,因为学号可能带字母或前导零。

CREATE DATABASE student_mgr DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE student_mgr; CREATE TABLE class ( class_id INT PRIMARY KEY AUTO_INCREMENT, class_name VARCHAR(50) NOT NULL ); CREATE TABLE student ( stu_id VARCHAR(20) PRIMARY KEY, stu_name VARCHAR(50) NOT NULL, gender CHAR(1) DEFAULT '男', age INT DEFAULT 0, class_id INT, enroll_date DATE, phone VARCHAR(20), FOREIGN KEY (class_id) REFERENCES class(class_id) );

建表时几个参数值得说清楚。utf8mb4是为了存中文和特殊符号,utf8在三字节以上字符会出问题。age设默认值 0 而不是 NULL,后面统计平均年龄时少一层判空。外键约束看情况加,课程设计里加上能体现表关系,但插入数据时要注意先插班级再插学生,否则外键报错。

注意:如果导入 SQL 时提示ERROR 1064,先检查是不是用了 MySQL 5.7 的语法去跑 8.0 的脚本,或者反过来的情况。utf8mb4_0900_ai_ci这个排序规则只有 8.0 才有,5.7 要用utf8mb4_general_ci。

2.3 在 IDEA 里配置 JDBC 驱动

MySQL 8.0 的驱动包是mysql-connector-j-8.x.x.jar,去 MySQL 官网下载或者用 Maven 拉。手动加的话,在 IDEA 项目结构里找到 Libraries,点加号选 Java,把 jar 包加进去。加完之后写一行测试代码验证驱动能不能加载:

public class DbTest { public static void main(String[] args) { try { Class.forName("com.mysql.cj.jdbc.Driver"); System.out.println("驱动加载成功"); } catch (ClassNotFoundException e) { System.out.println("驱动加载失败:" + e.getMessage()); } } }

Class.forName这行在 JDBC 4.0 以后其实可以省略,驱动会自动注册,但显式写出来能帮你确认 jar 包有没有加对。如果报ClassNotFoundException,九成是 jar 包没进 classpath,检查 IDEA 的 Libraries 里有没有这个包,或者 Maven 的pom.xml里依赖有没有写对。MySQL 8.0 的驱动类名是com.mysql.cj.jdbc.Driver,不是老版本的com.mysql.jdbc.Driver,写错了也会报这个错。

3. JDBC 增删改查:把 DAO 层写扎实

3.1 获取数据库连接的三种方式与选型

最原始的方式是每次操作都DriverManager.getConnection,代码简单但性能差,高并发下连接创建开销大。第二种是用DataSource,比如 MySQL 官方提供的MysqlDataSource,可以配置连接参数。第三种是连接池,比如 HikariCP 或 Druid,课程设计里用 HikariCP 就够,配置少、性能好。

我一般先写一个DBUtil类把连接逻辑收口,后面换连接池只改这一个地方:

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String URL = "jdbc:mysql://localhost:3306/student_mgr?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "你的密码"; public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn) { if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

URL 里三个参数必须说清楚。useSSL=false是关掉 SSL 连接,本地开发不关会报MySQL SSL connection error,因为 8.0 默认要求 SSL 但本地没配证书。serverTimezone=Asia/Shanghai是解决时区问题,不写的话插入日期可能差 8 小时。characterEncoding=utf8保证中文不乱码。这三个参数是血泪经验,少一个都可能翻车。

提示:如果报This version of the JDBC driver is only compatible with...,说明驱动版本和数据库版本不匹配。MySQL 8.0 数据库要用 8.0 以上的驱动,5.7 数据库用 5.1 驱动,别混用。

3.2 学生信息的新增与查询:PreparedStatement 的正确用法

增删改查里最核心的是PreparedStatement,它预编译 SQL,能防注入,还能处理参数类型。先写新增:

public boolean addStudent(Student stu) { String sql = "INSERT INTO student(stu_id, stu_name, gender, age, class_id, enroll_date, phone) VALUES(?,?,?,?,?,?,?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, stu.getStuId()); ps.setString(2, stu.getStuName()); ps.setString(3, stu.getGender()); ps.setInt(4, stu.getAge()); ps.setInt(5, stu.getClassId()); ps.setDate(6, java.sql.Date.valueOf(stu.getEnrollDate())); ps.setString(7, stu.getPhone()); return ps.executeUpdate() > 0; } catch (SQLException e) { e.printStackTrace(); return false; } }

try-with-resources写法保证连接和语句自动关闭,不用手动写 finally。setDate那里用java.sql.Date.valueOf把字符串转成日期,比SimpleDateFormat再转更直接。executeUpdate返回影响行数,大于 0 说明插入成功。

查询用executeQuery返回ResultSet,遍历时按列名取值:

public List<Student> findAll() { List<Student> list = new ArrayList<>(); String sql = "SELECT * FROM student ORDER BY stu_id"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql); ResultSet rs = ps.executeQuery()) { while (rs.next()) { Student stu = new Student(); stu.setStuId(rs.getString("stu_id")); stu.setStuName(rs.getString("stu_name")); stu.setGender(rs.getString("gender")); stu.setAge(rs.getInt("age")); stu.setClassId(rs.getInt("class_id")); stu.setEnrollDate(rs.getDate("enroll_date").toString()); stu.setPhone(rs.getString("phone")); list.add(stu); } } catch (SQLException e) { e.printStackTrace(); } return list; }

按列名取值比按索引取值可读性好,索引写错了不容易发现。rs.getDate返回java.sql.Date,转成字符串方便界面显示。查询结果封装成List<Student>,Swing 的表格模型可以直接用这个列表填充数据。

3.3 修改与删除:事务边界和影响行数判断

修改和删除逻辑类似,关键是判断影响行数。如果更新时学号不存在,executeUpdate返回 0,这时候要告诉用户没找到记录,而不是默默成功。

public boolean updateStudent(Student stu) { String sql = "UPDATE student SET stu_name=?, gender=?, age=?, class_id=?, enroll_date=?, phone=? WHERE stu_id=?"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, stu.getStuName()); ps.setString(2, stu.getGender()); ps.setInt(3, stu.getAge()); ps.setInt(4, stu.getClassId()); ps.setDate(5, java.sql.Date.valueOf(stu.getEnrollDate())); ps.setString(6, stu.getPhone()); ps.setString(7, stu.getStuId()); int rows = ps.executeUpdate(); return rows > 0; } catch (SQLException e) { e.printStackTrace(); return false; } }

删除同理,DELETE FROM student WHERE stu_id=?,返回行数大于 0 才算删成功。这里有个容易忽略的点:如果开了事务,executeUpdate之后要commit,否则数据不会真正落库。课程设计里如果只做单表操作,可以不开事务,默认自动提交。但如果涉及多表操作,比如删班级同时删学生,就必须手动控制事务。

public boolean deleteClassWithStudents(int classId) { String deleteStudents = "DELETE FROM student WHERE class_id=?"; String deleteClass = "DELETE FROM class WHERE class_id=?"; Connection conn = null; try { conn = DBUtil.getConnection(); conn.setAutoCommit(false); try (PreparedStatement ps1 = conn.prepareStatement(deleteStudents); PreparedStatement ps2 = conn.prepareStatement(deleteClass)) { ps1.setInt(1, classId); ps1.executeUpdate(); ps2.setInt(1, classId); ps2.executeUpdate(); conn.commit(); return true; } catch (SQLException e) { conn.rollback(); e.printStackTrace(); return false; } } catch (SQLException e) { e.printStackTrace(); return false; } finally { if (conn != null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }

事务这段代码是面试高频考点,也是实际项目里保证数据一致性的基本手段。setAutoCommit(false)开启事务,所有操作成功后commit,任何一步失败就rollback。最后在 finally 里恢复自动提交并关闭连接,避免连接放回池里还带着未提交的事务。

4. Swing 界面与 JDBC 的衔接:让数据流动起来

4.1 用 JTable 展示学生列表并绑定查询结果

Swing 里展示表格用JTable,数据模型用DefaultTableModel。查询结果拿到List<Student>后,清空表格模型再逐行添加:

String[] columns = {"学号", "姓名", "性别", "年龄", "班级ID", "入学日期", "电话"}; DefaultTableModel model = new DefaultTableModel(columns, 0); JTable table = new JTable(model); public void refreshTable() { model.setRowCount(0); List<Student> list = studentDao.findAll(); for (Student stu : list) { model.addRow(new Object[]{ stu.getStuId(), stu.getStuName(), stu.getGender(), stu.getAge(), stu.getClassId(), stu.getEnrollDate(), stu.getPhone() }); } }

setRowCount(0)是清空表格,比重新创建DefaultTableModel更高效。每次增删改之后调用refreshTable(),界面就能同步最新数据。这里要注意 Swing 的线程模型,数据库操作不能放在事件分发线程里,否则界面会卡死。简单做法是增删改查放在独立线程里执行,完成后用SwingUtilities.invokeLater更新界面。

4.2 表单输入校验与 SQL 异常处理

界面上用户输入的数据不能直接塞进 SQL,要先做校验。学号不能为空、年龄必须是数字、日期格式要正确。校验不通过就弹窗提示,不要等到 JDBC 报错才处理。

public boolean validateInput(String stuId, String name, String ageStr, String dateStr) { if (stuId == null || stuId.trim().isEmpty()) { JOptionPane.showMessageDialog(null, "学号不能为空"); return false; } try { int age = Integer.parseInt(ageStr); if (age < 0 || age > 150) { JOptionPane.showMessageDialog(null, "年龄不合法"); return false; } } catch (NumberFormatException e) { JOptionPane.showMessageDialog(null, "年龄必须是数字"); return false; } try { java.sql.Date.valueOf(dateStr); } catch (IllegalArgumentException e) { JOptionPane.showMessageDialog(null, "日期格式应为 yyyy-MM-dd"); return false; } return true; }

SQL 异常处理要分类型。主键冲突报SQLIntegrityConstraintViolationException,提示用户学号已存在。外键约束报错说明班级 ID 不存在。连接超时报SQLTimeoutException,检查数据库服务。把异常信息转成用户能看懂的中文提示,而不是直接弹堆栈。

注意:Swing 里所有弹窗和界面更新都要在事件分发线程里做,数据库查询在后台线程做。混在一起写,界面会假死,用户以为程序崩了。

5. 避坑与排查:那些让我熬夜的 JDBC 问题

5.1 连接报错:SSL、时区和驱动版本

现象:运行程序报MySQL SSL connection error或The server time zone value '?D1ú±ê×?ê±??' is unrecognized。原因:MySQL 8.0 默认要求 SSL 连接,且服务器时区没配。解决:JDBC URL 里加useSSL=false&serverTimezone=Asia/Shanghai。如果还报驱动版本不兼容,检查mysql-connector-j的版本是不是 8.0 以上。

5.2 中文乱码:字符集三层配置

现象:插入中文后数据库里显示问号,或者查询出来是乱码。原因:数据库、表、连接三层的字符集不一致。解决:建库时用utf8mb4,建表时继承库的字符集,JDBC URL 里加characterEncoding=utf8。三层都对齐了才不会乱码。如果已经建了表,用ALTER TABLE student CONVERT TO CHARACTER SET utf8mb4改。

5.3 连接泄漏:忘记关闭 ResultSet 和 Statement

现象:程序跑一段时间后报Too many connections。原因:每次查询都创建连接但没关闭,连接数耗尽。解决:用 try-with-resources 自动关闭,或者 finally 里显式关闭。ResultSet、Statement、Connection三个都要关,关闭顺序和创建顺序相反。用连接池的话,close是归还连接而不是真正关闭,但同样必须调用。

5.4 事务未提交:数据“消失”了

现象:代码执行没报错,但数据库里查不到新插入的数据。原因:开了事务但忘记commit,或者连接关闭时自动回滚了。解决:检查setAutoCommit(false)之后有没有对应的commit。用 try-with-resources 时,如果commit写在 try 块里,异常发生时不会执行,要在 catch 里rollback。

5.5 主键冲突与空指针:输入校验不能省

现象:插入时报Duplicate entry或者程序抛NullPointerException。原因:学号重复,或者界面输入框为空直接传给了 JDBC。解决:插入前先查一次学号是否存在,或者捕获主键冲突异常给用户提示。所有界面输入都要判空,PreparedStatement.setString传 null 虽然不报错,但数据库字段如果是 NOT NULL 就会插入失败。

6. 进阶技巧:用连接池和批量操作提升系统可用性

课程设计做完基本功能后,如果想再往上走一步,有两个方向值得投入:连接池和批量操作。连接池用 HikariCP,配置简单,性能提升明显。把DBUtil改成从HikariDataSource获取连接:

import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; public class DBUtil { private static HikariDataSource dataSource; static { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/student_mgr?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8"); config.setUsername("root"); config.setPassword("你的密码"); config.setMaximumPoolSize(10); config.setMinimumIdle(2); config.setConnectionTimeout(30000); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }

maximumPoolSize设 10 对课程设计足够,minimumIdle设 2 保证有空闲连接。connectionTimeout是获取连接的超时时间,超过 30 秒还没拿到就抛异常,避免线程一直等。连接池的好处是连接复用,不用每次操作都走 TCP 握手和认证,批量导入学生数据时差距非常明显。

批量操作用addBatch和executeBatch,比如一次导入 100 个学生:

public void batchAdd(List<Student> students) { String sql = "INSERT INTO student(stu_id, stu_name, gender, age, class_id, enroll_date, phone) VALUES(?,?,?,?,?,?,?)"; try (Connection conn = DBUtil.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { conn.setAutoCommit(false); for (Student stu : students) { ps.setString(1, stu.getStuId()); ps.setString(2, stu.getStuName()); ps.setString(3, stu.getGender()); ps.setInt(4, stu.getAge()); ps.setInt(5, stu.getClassId()); ps.setDate(6, java.sql.Date.valueOf(stu.getEnrollDate())); ps.setString(7, stu.getPhone()); ps.addBatch(); } ps.executeBatch(); conn.commit(); } catch (SQLException e) { e.printStackTrace(); } }

批量插入比逐条插入快一个数量级,因为减少了网络往返。注意批量操作也要放在事务里,全部成功才提交,有一条失败就回滚。executeBatch返回的是每条语句影响行数的数组,可以遍历检查有没有插入失败的。

验证系统稳定性有个简单方法:写一个测试类,循环插入 1000 条学生记录,然后查询总数、随机删除、再查询,看数据是否一致。如果连接池配置不对,跑几百条就会报连接超时。如果事务没处理好,删除后可能残留脏数据。这个测试跑通,系统基本就能拿得出手了。

我自己做课程设计时最大的教训是:别急着拖界面,先把 DAO 层写完整、测通过,界面只是数据的展示层。JDBC 这层写扎实了,后面换 Web 框架还是换桌面框架,核心逻辑都能复用。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询