ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java+MySQL学生信息管理系统:JDBC增删改查与Swing界面实战

Java+MySQL学生信息管理系统:JDBC增删改查与Swing界面实战 简介这份资源面向Java初学者与需要完成课程设计的学生提供一套基于Java Swing与MySQL的学生信息管理系统实现方案重点解决使用JDBC对学生数据做增删改查的入门实践问题。压缩包内共1个PDF文件约135KB以图文形式呈现项目结构、数据库建表语句与核心代码片段便于对照理解model-dao-view三层划分。内容涵盖管理员与学生两类实体建模、admin与student表的字段设计以及登录、信息维护等基础功能的实现思路适合作为课设参考或自学练手素材。目前已有5010人学习下载说明其在同类入门项目中具备一定参考价值。读者可从中获取数据库脚本、实体类定义与分层组织方式快速搭建可运行的管理系统雏形并据此扩展查询、统计等模块。1. JavaMySQL 学生信息管理系统从课程设计到能跑通的增删改查课程设计里十个有八个是学生信息管理系统但真正能拿得出手、经得起答辩追问的不到三成。大部分项目卡在同一个地方界面能点数据库连不上或者连上了一改数据就报 SQLException。这个标题背后其实是一条完整的 Java 桌面开发链路——Swing 做界面、JDBC 做数据通道、MySQL 做持久化三者缺一不可。它适合正在做课程设计的学生也适合想用一个小项目把 JDBC 增删改查、事务、连接池串起来的 Java 初学者。我见过太多人把时间花在拖控件上结果 JDBC 那层写得千疮百孔换个环境就翻车。这篇笔记就按我实际做项目的顺序把选型、建表、写 DAO、调参数、排错一条线讲清楚让你做出来的东西不只是能演示而是能讲明白每一行代码为什么这么写。2. 环境搭建与数据库设计先把地基打牢2.1 JDK、MySQL 与 IDEA 的版本选择动手之前先把工具链定下来不然后面全是玄学问题。JDK 选 17 或 21 都行LTS 版本稳定别用太老的 8Swing 在高版本 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): Cant connect to local MySQL server through socket先检查 MySQL 服务有没有启动Windows 在服务管理器里找 MySQL80Linux 用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?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8; 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 里三个参数必须说清楚。useSSLfalse是关掉 SSL 连接本地开发不关会报MySQL SSL connection error因为 8.0 默认要求 SSL 但本地没配证书。serverTimezoneAsia/Shanghai是解决时区问题不写的话插入日期可能差 8 小时。characterEncodingutf8保证中文不乱码。这三个参数是血泪经验少一个都可能翻车。提示如果报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 ListStudent findAll() { ListStudent 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转成字符串方便界面显示。查询结果封装成ListStudentSwing 的表格模型可以直接用这个列表填充数据。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。查询结果拿到ListStudent后清空表格模型再逐行添加String[] columns {学号, 姓名, 性别, 年龄, 班级ID, 入学日期, 电话}; DefaultTableModel model new DefaultTableModel(columns, 0); JTable table new JTable(model); public void refreshTable() { model.setRowCount(0); ListStudent 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 里加useSSLfalseserverTimezoneAsia/Shanghai。如果还报驱动版本不兼容检查mysql-connector-j的版本是不是 8.0 以上。5.2 中文乱码字符集三层配置现象插入中文后数据库里显示问号或者查询出来是乱码。原因数据库、表、连接三层的字符集不一致。解决建库时用utf8mb4建表时继承库的字符集JDBC URL 里加characterEncodingutf8。三层都对齐了才不会乱码。如果已经建了表用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?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8); 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(ListStudent 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 框架还是换桌面框架核心逻辑都能复用。希望帮到你。本文还有配套的精品资源点击获取
返回列表