尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

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

Java+MySQL学生信息管理系统:JDBC增删改查与Swing界面完整实现 简介这份资源面向Java初学者与需要完成课程设计的学生提供一套基于Java Swing与MySQL的学生信息管理系统实现方案重点解决JDBC对学生数据的增删改查操作适合作为课设参考或入门练手项目。压缩包内共1个PDF文件约135KB内容涵盖开发环境说明JDK7MySQL5Win7、数据库建库建表语句、model-dao-view三层代码结构以及管理员与学生实体类的定义读者可据此理解项目分层思路与数据库设计方法。目前已有5010人学习下载说明该方案在同类课设场景中具有较高的参考价值。通过阅读读者能够掌握JDBC连接数据库、Swing界面搭建与基础CRUD逻辑的整合方式并可直接参考建表语句与实体类代码快速搭建自己的学生信息管理项目为后续功能扩展打下基础。1. JavaMySQL 学生信息管理系统从课程设计到能跑通增删改查的完整落地路径很多同学做 JavaMySQL 学生信息管理系统卡住的地方从来不是“不会写代码”而是环境没搭好、JDBC 连接报错、Swing 界面和数据库对不上号。这个标题背后其实是一条非常典型的 JavaWeb 入门链路用 Java 做业务逻辑用 MySQL 存数据用 JDBC 打通两者再用 Swing 或控制台把增删改查暴露出来。它适合课程设计、Java 基础练手、面试前想补一个完整项目经验的人。我见过太多人 JDBC 驱动版本和 MySQL 8 对不上或者Class.forName加载了旧驱动结果连不上数据库直接翻车。这篇就按“能复现”的标准把建库建表、JDBC 工具类、DAO 层、Swing 界面和排错一条线讲清楚让你照着敲完就能跑起来。2. 环境搭建与数据库设计MySQL 8 安装配置和建表脚本2.1 MySQL 8 安装配置教程里最容易忽略的三个参数Windows 上装 MySQL 8官网下载安装包一路下一步基本没问题但有几个点不设好后面 JDBC 连接必出问题。第一个是认证插件MySQL 8 默认用caching_sha2_password老版本 JDBC 驱动不认会报Unable to load authentication plugin。第二个是字符集建库时不指定utf8mb4中文姓名存进去就是问号。第三个是时区不设serverTimezone连接时会抛The server time zone value is unrecognized。Linux 上用 rpm 安装 MySQL 也类似装完先跑mysqld --initialize初始化数据目录再启动服务。我一般会先确认端口 3306 没被占用然后用mysql -u root -p登录验证。# 查看 MySQL 服务状态Linux systemctl status mysqld # 登录并查看当前认证插件 mysql -u root -p SELECT user, host, plugin FROM mysql.user WHERE userroot;如果 plugin 显示caching_sha2_password而你的 JDBC 驱动是 5.x 版本要么升级驱动到 8.x要么改认证方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;提示生产环境不建议用mysql_native_password但课程设计本地跑这样最省事。2.2 学生信息管理系统的建库建表脚本数据库设计不用太复杂一张学生表加一张用户表就够了。学生表存学号、姓名、性别、班级、专业、入学日期用户表存登录账号密码。学号做主键避免用自增 id 当业务主键因为学号本身唯一且有意义。CREATE DATABASE student_ms DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_ms; CREATE TABLE student ( sno VARCHAR(20) PRIMARY KEY COMMENT 学号, name VARCHAR(50) NOT NULL COMMENT 姓名, gender VARCHAR(4) DEFAULT 男 COMMENT 性别, class_name VARCHAR(50) COMMENT 班级, major VARCHAR(50) COMMENT 专业, enroll_date DATE COMMENT 入学日期 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE sys_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) UNIQUE NOT NULL, password VARCHAR(64) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; INSERT INTO sys_user (username, password) VALUES (admin, 123456);建表时ENGINEInnoDB必须写因为后面如果要加事务MyISAM 不支持。utf8mb4而不是utf8是因为 MySQL 的utf8其实是阉割版存不了 emoji 和部分生僻字。字段长度别抠太死VARCHAR(20)存学号足够但姓名给VARCHAR(50)留余量复姓加少数民族名字可能超。2.3 JDBC 驱动引入和 IDEA 里的常见坑IntelliJ IDEA 里加 MySQL 驱动有两种方式Maven 依赖或者手动加 jar。用 Maven 最省心在pom.xml里加dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency手动加 jar 的话下载mysql-connector-java-8.0.33.jar在 Project Structure 的 Libraries 里加进去。我遇到过 IDEA 自动下载 Maven 依赖失败的情况报download from maven failed一般是网络问题换成阿里云镜像仓库就好mirror idaliyun/id mirrorOfcentral/mirrorOf urlhttps://maven.aliyun.com/repository/public/url /mirror驱动版本和 MySQL 版本要匹配。MySQL 8 配 connector 8.xMySQL 5.7 配 connector 5.1.x。版本不匹配最典型的报错就是this version of the JDBC driver is only compatible with...看到这个先查版本对应关系别急着改代码。3. JDBC 增删改查从工具类到 DAO 层的完整实现3.1 JDBC 工具类的封装与连接参数说明每次增删改查都写一遍DriverManager.getConnection是新手最常见的重复代码。正确做法是抽一个DBUtil工具类把 URL、用户名、密码集中管理连接获取和释放统一处理。import java.sql.*; public class DBUtil { // MySQL 8 的 URL 必须带 serverTimezone否则报时区错误 private static final String URL jdbc:mysql://localhost:3306/student_ms ?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8mb4; private static final String USER root; private static final String PASSWORD 你的密码; static { try { // MySQL 8 驱动类名是 com.mysql.cj.jdbc.Driver // 旧版是 com.mysql.jdbc.Driver写错会报 ClassNotFoundException Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { throw new RuntimeException(MySQL 驱动加载失败, e); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, Statement stmt, ResultSet rs) { // 关闭顺序ResultSet - Statement - Connection try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (stmt ! null) stmt.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }URL 里三个参数必须说清楚useSSLfalse关掉 SSL 连接本地开发不需要不关会报mysql ssl连接错误serverTimezoneAsia/Shanghai指定时区不写就抛时区异常characterEncodingutf8mb4保证中文不乱码。密码别硬编码在代码里课程设计可以接受但要知道生产环境应该走配置文件或环境变量。3.2 学生表的增删改查PreparedStatement 的正确用法DAO 层负责所有数据库操作。用PreparedStatement而不是Statement一是防 SQL 注入二是预编译性能更好。下面把四个操作都写全。import java.sql.*; import java.util.ArrayList; import java.util.List; public class StudentDao { // 新增学生 public boolean add(Student s) { String sql INSERT INTO student(sno, name, gender, class_name, 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.setString(3, s.getGender()); ps.setString(4, s.getClassName()); ps.setString(5, s.getMajor()); ps.setDate(6, Date.valueOf(s.getEnrollDate())); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } } // 根据学号删除 public boolean delete(String sno) { String sql DELETE FROM student WHERE sno ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, sno); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } } // 修改学生信息 public boolean update(Student s) { String sql UPDATE student SET name?, gender?, class_name?, major?, enroll_date? WHERE sno?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, s.getName()); ps.setString(2, s.getGender()); ps.setString(3, s.getClassName()); ps.setString(4, s.getMajor()); ps.setDate(5, Date.valueOf(s.getEnrollDate())); ps.setString(6, s.getSno()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } } // 查询全部 public ListStudent findAll() { ListStudent list new ArrayList(); String sql SELECT * FROM student ORDER BY sno; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Student s new Student(); s.setSno(rs.getString(sno)); s.setName(rs.getString(name)); s.setGender(rs.getString(gender)); s.setClassName(rs.getString(class_name)); s.setMajor(rs.getString(major)); s.setEnrollDate(rs.getDate(enroll_date).toLocalDate()); list.add(s); } } catch (SQLException e) { e.printStackTrace(); } return list; } }这里用了 try-with-resources连接和语句会自动关闭比手动 close 更安全。ps.setDate(6, Date.valueOf(...))是把LocalDate转成java.sql.Date直接传LocalDate会编译不过。查询返回ListStudent实体类字段和表字段一一对应class_name映射到className命名风格差异在 setter 里处理。3.3 事务控制JDBC transaction 保证数据一致性如果业务里涉及多表操作比如删除学生同时要删他的选课记录就必须用事务。JDBC 默认自动提交每条 SQL 执行完就生效中途出错前面的操作回不去。public boolean deleteWithTransaction(String sno) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交开启事务 String sql1 DELETE FROM score WHERE sno ?; String sql2 DELETE FROM student WHERE sno ?; try (PreparedStatement ps1 conn.prepareStatement(sql1); PreparedStatement ps2 conn.prepareStatement(sql2)) { ps1.setString(1, sno); ps1.executeUpdate(); ps2.setString(1, sno); ps2.executeUpdate(); } conn.commit(); // 两条都成功才提交 return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } e.printStackTrace(); return false; } finally { if (conn ! null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }关键点setAutoCommit(false)之后所有 SQL 都在同一个事务里commit()才真正落库任何一步抛异常就rollback()。finally 里恢复自动提交再关闭连接避免连接池回收后状态混乱。Java 怎么保证数据一致性在单机 JDBC 层面就是靠事务的 ACID别想太复杂。4. Swing 界面与业务整合把控制台程序变成可视化系统4.1 Java Swing 主界面布局与表格绑定Swing 虽然老但课程设计够用。主界面用JFrame上面放JTable显示学生列表下面放增删改查按钮。JTable的数据用DefaultTableModel和 DAO 查出来的ListStudent对接。import javax.swing.*; import javax.swing.table.DefaultTableModel; import java.awt.*; import java.util.List; public class MainFrame extends JFrame { private JTable table; private DefaultTableModel model; private StudentDao dao new StudentDao(); public MainFrame() { setTitle(学生信息管理系统); setSize(800, 500); setDefaultCloseOperation(EXIT_ON_CLOSE); setLocationRelativeTo(null); // 表头和数据库字段对应 String[] columns {学号, 姓名, 性别, 班级, 专业, 入学日期}; model new DefaultTableModel(columns, 0) { Override public boolean isCellEditable(int row, int column) { return false; // 表格只读编辑走弹窗 } }; table new JTable(model); JScrollPane scrollPane new JScrollPane(table); add(scrollPane, BorderLayout.CENTER); // 按钮面板 JPanel btnPanel new JPanel(); JButton addBtn new JButton(新增); JButton delBtn new JButton(删除); JButton refreshBtn new JButton(刷新); btnPanel.add(addBtn); btnPanel.add(delBtn); btnPanel.add(refreshBtn); add(btnPanel, BorderLayout.SOUTH); // 刷新按钮绑定查询 refreshBtn.addActionListener(e - loadData()); // 删除按钮取选中行的学号 delBtn.addActionListener(e - { int row table.getSelectedRow(); if (row -1) { JOptionPane.showMessageDialog(this, 请先选中一行); return; } String sno (String) model.getValueAt(row, 0); if (dao.delete(sno)) { JOptionPane.showMessageDialog(this, 删除成功); loadData(); } else { JOptionPane.showMessageDialog(this, 删除失败); } }); loadData(); setVisible(true); } private void loadData() { model.setRowCount(0); // 清空旧数据 ListStudent list dao.findAll(); for (Student s : list) { model.addRow(new Object[]{ s.getSno(), s.getName(), s.getGender(), s.getClassName(), s.getMajor(), s.getEnrollDate() }); } } public static void main(String[] args) { // Swing 组件必须在事件分发线程创建 SwingUtilities.invokeLater(MainFrame::new); } }model.setRowCount(0)是刷新前清空表格不清空会重复追加。isCellEditable返回 false 让表格只读编辑走独立弹窗避免用户直接改表格导致数据不同步。SwingUtilities.invokeLater是 Swing 的线程规范所有 UI 操作放事件分发线程不写虽然多数时候能跑但属于埋雷。4.2 新增和修改弹窗数据校验与 DAO 调用新增和修改共用一个弹窗传入Student对象决定是插入还是更新。学号在修改时不可编辑因为它是主键。public class StudentDialog extends JDialog { private JTextField snoField, nameField, classField, majorField; private JComboBoxString genderBox; private JTextField dateField; private boolean confirmed false; private Student student; private StudentDao dao new StudentDao(); public StudentDialog(JFrame parent, Student stu) { super(parent, stu null ? 新增学生 : 修改学生, true); this.student stu; setSize(350, 300); setLocationRelativeTo(parent); setLayout(new GridLayout(7, 2, 5, 5)); add(new JLabel(学号:)); snoField new JTextField(); add(snoField); add(new JLabel(姓名:)); nameField new JTextField(); add(nameField); add(new JLabel(性别:)); genderBox new JComboBox(new String[]{男, 女}); add(genderBox); add(new JLabel(班级:)); classField new JTextField(); add(classField); add(new JLabel(专业:)); majorField new JTextField(); add(majorField); add(new JLabel(入学日期(yyyy-MM-dd):)); dateField new JTextField(); add(dateField); JButton okBtn new JButton(确定); JButton cancelBtn new JButton(取消); add(okBtn); add(cancelBtn); // 修改模式回填数据学号不可改 if (stu ! null) { snoField.setText(stu.getSno()); snoField.setEditable(false); nameField.setText(stu.getName()); genderBox.setSelectedItem(stu.getGender()); classField.setText(stu.getClassName()); majorField.setText(stu.getMajor()); dateField.setText(stu.getEnrollDate().toString()); } okBtn.addActionListener(e - { if (snoField.getText().trim().isEmpty() || nameField.getText().trim().isEmpty()) { JOptionPane.showMessageDialog(this, 学号和姓名不能为空); return; } Student s new Student(); s.setSno(snoField.getText().trim()); s.setName(nameField.getText().trim()); s.setGender((String) genderBox.getSelectedItem()); s.setClassName(classField.getText().trim()); s.setMajor(majorField.getText().trim()); try { s.setEnrollDate(java.time.LocalDate.parse(dateField.getText().trim())); } catch (Exception ex) { JOptionPane.showMessageDialog(this, 日期格式错误应为 yyyy-MM-dd); return; } boolean ok (stu null) ? dao.add(s) : dao.update(s); if (ok) { confirmed true; dispose(); } else { JOptionPane.showMessageDialog(this, 操作失败学号可能已存在); } }); cancelBtn.addActionListener(e - dispose()); } public boolean isConfirmed() { return confirmed; } }校验分两层非空校验在弹窗里做唯一性校验靠数据库主键约束插入重复学号会抛异常DAO 返回 false弹窗提示“学号可能已存在”。日期解析用LocalDate.parse格式不对直接捕获异常提示用户别让程序崩掉。5. 避坑与排查JDBC 连不上、中文乱码、Swing 卡死的处理5.1 连接报错 error 2002 和 socket 问题现象运行程序抛error 2002 (hy000): cant connect to local mysql server through socket /tmp/mysql.sock。原因MySQL 服务没启动或者 JDBC URL 里用了 socket 连接方式但路径不对。解决先systemctl start mysqld确认服务在跑然后检查 URL 是不是写成了jdbc:mysql://localhost:3306本地连接走 TCP 不走 socket。如果服务确实起了还报这个看 MySQL 配置文件里socket路径和客户端默认路径是否一致。5.2 中文乱码从建库到 JDBC 的字符集链路现象姓名存进去变成???或者乱码。原因字符集在某一环断了。建库没用utf8mb4或者 JDBC URL 没加characterEncodingutf8mb4或者 IDEA 文件编码不是 UTF-8。解决按链路逐环检查——SHOW CREATE DATABASE student_ms看字符集URL 补上characterEncodingutf8mb4IDEA 的 File Encoding 设成 UTF-8。三处都对了才不会乱码。5.3 驱动版本不匹配的典型报错现象ClassNotFoundException: com.mysql.jdbc.Driver或者Unable to load authentication plugin caching_sha2_password。原因驱动类名写的是旧版或者驱动版本低于 8.0 连 MySQL 8。解决MySQL 8 统一用com.mysql.cj.jdbc.DriverMaven 依赖升到 8.0.x。如果升级驱动后还报认证插件问题检查 MySQL 用户是不是caching_sha2_password临时改成mysql_native_password验证。5.4 Swing 界面卡死EDT 里做耗时操作现象点查询按钮后界面无响应转圈圈。原因数据库查询在事件分发线程EDT里执行网络或磁盘慢就阻塞 UI。解决耗时操作放SwingWorker的doInBackground里结果回done方法更新表格。课程设计数据量小可能感觉不到但这是 Swing 的硬规矩养成习惯。new SwingWorkerListStudent, Void() { Override protected ListStudent doInBackground() { return dao.findAll(); // 后台线程查数据库 } Override protected void done() { try { ListStudent list get(); model.setRowCount(0); for (Student s : list) { model.addRow(new Object[]{s.getSno(), s.getName(), s.getGender(), s.getClassName(), s.getMajor(), s.getEnrollDate()}); } } catch (Exception e) { e.printStackTrace(); } } }.execute();5.5 学号重复插入没提示现象新增相同学号程序没反应或者直接抛异常堆栈。原因DAO 里executeUpdate抛SQLIntegrityConstraintViolationException但没捕获处理。解决在add方法里单独捕获这个异常返回 false 并给出友好提示别把堆栈直接甩给用户。6. 进阶技巧用连接池和批量操作把系统做扎实课程设计跑通之后如果想让它更像回事有两个方向值得做。第一个是引入数据库连接池。现在每次操作都DriverManager.getConnection频繁创建销毁连接开销大。换成 HikariCP配置最小空闲连接和最大连接数性能提升明显。HikariConfig config new HikariConfig(); config.setJdbcUrl(jdbc:mysql://localhost:3306/student_ms?useSSLfalseserverTimezoneAsia/Shanghai); config.setUsername(root); config.setPassword(你的密码); config.setMaximumPoolSize(10); config.setMinimumIdle(2); HikariDataSource ds new HikariDataSource(config); // DBUtil.getConnection() 改成 return ds.getConnection();第二个是批量插入。如果要从 Excel 导入几百条学生记录逐条executeUpdate慢得离谱。用addBatch和executeBatch一次提交。public void batchAdd(ListStudent list) throws SQLException { String sql INSERT INTO student(sno, name, gender, class_name, major, enroll_date) VALUES(?,?,?,?,?,?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { conn.setAutoCommit(false); for (Student s : list) { ps.setString(1, s.getSno()); ps.setString(2, s.getName()); ps.setString(3, s.getGender()); ps.setString(4, s.getClassName()); ps.setString(5, s.getMajor()); ps.setDate(6, Date.valueOf(s.getEnrollDate())); ps.addBatch(); } ps.executeBatch(); conn.commit(); } }批量操作一定要关自动提交否则每条都提交一次批量就没意义了。executeBatch返回受影响行数数组可以校验是否全部成功。验证系统是否做扎实我一般会跑三个检查一是并发插入相同学号看唯一约束是否生效二是插入含中文和特殊字符的姓名看是否乱码三是断开 MySQL 服务后点查询看程序是否优雅提示而不是崩溃。这三个过了基本就能拿去答辩。我自己踩过最深的坑是早期用Statement拼 SQL学生姓名里带个单引号直接报语法错误后来全部换PreparedStatement才消停。还有一次忘了关连接跑了几百次查询后 MySQL 报Too many connections从那以后工具类里 close 方法再也不敢省。希望帮到你。本文还有配套的精品资源点击获取
返回列表