ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java+Swing+MySQL学生信息管理系统:环境配置与JDBC实战

Java+Swing+MySQL学生信息管理系统:环境配置与JDBC实战 简介面向Java基础阶段学习者的控制台学生信息管理系统基于Java SE MySQL实现单管理员模式支持登录验证、学生信息的增删改查可在Eclipse/MyEclipse中直接运行常用于课程设计参考。资源为RAR压缩包共33个文件约37.67MB包含完整Java源码与编译后的class文件、MySQL建表SQL脚本、两个版本MySQL连接驱动jar包以及运行截图、技术说明文档和操作演示视频方便从环境配置到功能运行全流程对照。当前已有66人学习下载。借助源码可掌握控制台与数据库交互的经典编程结构配合mp4演示和文档说明能快速理解各模块职责减少课程设计中常见的中文乱码、驱动连接等踩坑问题是Java入门项目练手与课设实现的实用资料。1. 基于 java 控件台 mysql 的学生信息管理系统为什么大多数课设都卡在环境上先说一个反直觉的结论大部分照着这个标题做学生信息管理系统的人翻车点不在增删改查的代码逻辑而在「java 能连上 mysql」这一步。你写好了窗口、摆好了控件、设计好了数据表结果一跑起来报ClassNotFoundException或者Communications link failure这时候你根本不知道是驱动没引进去、MySQL 服务没启动还是 JDBC 连接串写错了。这类项目说白了就是一个「JDBC Swing 控件台 MySQL」的标准三方组合java 负责逻辑和控件交互mysql 负责落库中间用 JDBC 搭桥。它的价值在于用最少的依赖把一条完整的数据链路走通——从窗口输入到数据库落盘再从库回显到表格适合做课设、毕设起步或者第一次想把「会写 java 语法」升级成「能做出一个能用的系统」。做这个项目的核心不是炫技而是把环境基线踩平、把 JDBC 的参数吃透、把表格数据和ResultSet的映射理顺。下面按我自己做这类项目的顺序来讲先铺环境再连库再写业务然后单独把踩过的坑列出来最后补一些能让系统拿得出手的细节。2. 把环境基线铺平JDK、MySQL 与驱动 jar 的选型和安装2.1 JDK 与 MySQL 版本怎么选才不折腾这个系统对 java 版本不敏感JDK 8 和 JDK 11 都行但我不建议一上来就装最新的 JDK 21 或 25。原因是很多教学环境、后续可能用到的Navicat连接工具、以及旧版mysql-connector驱动对太新的 JDK 反而会报一些奇怪的module访问限制。JDK 8 是国内课程设计的绝对主流教材里的javax.swing控件写法、Class.forName加载驱动的老写法在 JDK 8 下都是原样可用遇到问题也最好搜到答案。MySQL 的选择则要看你的使用场景。如果只是本地自己跑MySQL 5.7 最省心认证插件默认是mysql_native_password和几乎所有版本的驱动、可视化工具都兼容。MySQL 8.0 也完全能做但你必须处理两个新东西默认认证插件caching_sha2_password、以及 JDBC 连接串里必须要带的时区参数serverTimezone。很多人的SSL 连接错误和Public Key Retrieval is not allowed基本都是 MySQL 8.0 引起的5.7 上很少见。驱动 jar 的选择遵循一个简单原则如果你用 MySQL 5.7用mysql-connector-java5.1.x 系列最稳如果你用 MySQL 8.0就用mysql-connector-j8.0.x 系列。不要在 5.7 的库上硬上 8.x 驱动虽然 5.1.x 驱动也能连 8.0但需要额外给 root 改认证插件属于给自己挖坑。注意JDK 安装路径不要带空格和中文C:\Program Files\Java这种路径在部分老版本 JDK 下会引发脚本解析问题。我一般直接装在D:\JDK8这类无空格路径下。2.2 用 Navicat 先建库别让代码背建库的锅很多新手喜欢在 java 代码里用CREATE DATABASE IF NOT EXISTS建库我一般不建议这么做。学生信息管理系统本身只有几张表完全没有必要在业务代码里做 DDL数据定义语言操作。更靠谱的顺序是先用 Navicat 或命令行把库和表建好再用代码去连。在 Navicat 里新建数据库student_db字符集选择utf8mb4排序规则选utf8mb4_general_ci。utf8mb4能存 emoji 和生僻字比utf8更省心。然后执行下面的建表 SQLCREATE DATABASE IF NOT EXISTS student_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE student_db; CREATE TABLE student ( id INT AUTO_INCREMENT PRIMARY KEY COMMENT 学号自增主键, student_no VARCHAR(20) NOT NULL UNIQUE COMMENT 学号业务唯一键, name VARCHAR(50) NOT NULL COMMENT 姓名, gender TINYINT DEFAULT 1 COMMENT 性别1 男0 女, age INT DEFAULT 0 COMMENT 年龄, class_name VARCHAR(50) DEFAULT COMMENT 班级, phone VARCHAR(20) DEFAULT COMMENT 手机号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT学生信息表; CREATE TABLE admin_user ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码的 MD5 值 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT管理员表; INSERT INTO admin_user (username, password) VALUES (admin, 21232f297a57a5a743894a0e4a801fc3);上面这段 SQL 里有两个关键的建模细节。student_no设了UNIQUE这个约束能在应用层代码漏判重复时兜底防止同一个学号插入两次password字段存的是MD5(admin)的结果不是明文。注意这个系统只是课程设计级别用 MD5 做演示可以理解但你要知道生产环境密码存储必须用 BCrypt 这类带盐的慢哈希算法这也是面试容易被追问的地方为什么不能明文存密码。建好表后用 Navicat 新建一个连接测试一下主机127.0.0.1、端口3306、用户名root、密码填你安装时设的密码。如果 Navicat 能连上说明 MySQL 服务、账号、端口都没问题接下来代码里连不上就只剩驱动和连接串的问题了。2.3 导入驱动 jar 的正确姿势不是下载了就行这个项目里驱动 jar 的引入是新手第一个翻车点。如果你用 IDEA 且项目是普通 Java 工程非 Maven正确做法是把mysql-connector-java-xxx.jar复制到项目根目录下的lib文件夹然后右键点击 jar 选择Add as Library。如果少了最后一步代码里Class.forName(com.mysql.cj.jdbc.Driver)编译能通过但一运行就抛ClassNotFoundException这就是典型的「代码对、依赖没挂上」。如果你用 Maven 工程则在pom.xml里加dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency注意版本号5.1.49 对应 MySQL 5.7如果是 MySQL 8.0groupId 是com.mysqlartifactId 是mysql-connector-j版本用8.0.33。两个时代的驱动类名也不一样5.x 用com.mysql.jdbc.Driver8.x 必须用com.mysql.cj.jdbc.Driver写错就报ClassNotFoundException。这一点很多视频里没讲清楚等你对着旧代码抄的时候最容易栽在这。3. JDBC 连接与登录模块先跑通 ConnectionManager 再谈界面3.1 写一个不花哨但稳定的连接工具类界面上的控件还没必要先写我一般先写一个DBUtil或ConnectionManager类把连接逻辑固定住后面所有业务代码都通过它拿Connection。这个类不需要任何框架用 JDBC 原生写法就够。import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBUtil { private static final String DRIVER com.mysql.jdbc.Driver; // MySQL 5.7 驱动类 private static final String URL jdbc:mysql://127.0.0.1:3306/student_db ?useUnicodetruecharacterEncodingutf8useSSLfalse; private static final String USER root; private static final String PASSWORD 你的数据库密码; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } public static void close(Connection conn, java.sql.Statement stmt, java.sql.ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } }这个类的逻辑分三层静态块里Class.forName把驱动加载进 JVM这一步保证后续DriverManager能识别jdbc:mysql://这种协议getConnection每次调用都建立一条物理连接close负责按ResultSet → Statement → Connection的顺序反向释放资源。很多人会把close顺序写反先关Connection再关ResultSet虽然 JDBC 规范里关闭Connection会自动释放关联资源但代码规范上必须先关从属对象。连接串里的参数逐个说。useSSLfalse是关闭 SSL 加密通道本地开发环境没有配置证书不关会报SSL connection errorcharacterEncodingutf8保证中文从窗口到数据库不乱码useUnicodetrue是characterEncoding的前置开关JDBC 规范里要配套写。如果你用的是 MySQL 8.0URL 里还要额外追加serverTimezoneAsia/Shanghai否则驱动会拿默认时区去解析DATETIME导致时间字段偏移 8 小时或者直接报错。3.2 登录校验别用 Statement 拼接小心 SQL 注入登录功能是这个系统的门面后台逻辑就是查admin_user表。但这里有个最常见的教学级错误直接用Statement拼字符串查询。比如String sql SELECT * FROM admin_user WHERE username username AND password password ;这种写法在课设答辩时被老师问「SQL 注入怎么防」会很难看更重要的是它确实不安全。正确做法是PreparedStatement预编译加占位符public boolean login(String username, String password) { String md5Pwd MD5Util.md5(password); String sql SELECT id FROM admin_user WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, md5Pwd); try (ResultSet rs ps.executeQuery()) { return rs.next(); } } catch (SQLException e) { e.printStackTrace(); return false; } }PreparedStatement的两个好处一是参数无论输入什么都只被当作字符串字面量不会参与 SQL 语法解析从根上杜绝注入二是同一条 SQL 多次执行时数据库端有预编译缓存性能更好。setString的索引从 1 开始很多人习惯从 0 开始写会直接报IndexOutOfBoundsException这也是新手的经典报错。另外注意密码的 MD5 转换放在 java 层做而不是把明文传进 SQL 里让MD5()函数处理。原因很简单SQL 里写MD5(password)的话password 是拼接进去的又回到注入的老路上了。自己写一个MD5Util用MessageDigest.getInstance(MD5)做摘要再转成 16 进制字符串。这段逻辑不复杂但建议独立成工具类因为在后面的「新增管理员」功能里还会用到。3.3 从登录窗口跳转到主界面登录成功后的跳转逻辑我习惯写成先dispose()当前窗口再new MainFrame().setVisible(true)。这里有个细节容易被忽略——dispose()只释放当前窗口资源JVM 不会退出所以主界面要设置默认关闭方式为EXIT_ON_CLOSE否则关掉主窗口后 java 进程还赖在后台表现为「关了窗口但控制台一直挂着」。登录失败时的提示不要只在控制台打印要在控件上给用户反馈。用JOptionPane.showMessageDialog(this, 用户名或密码错误, 登录失败, JOptionPane.ERROR_MESSAGE)。这句代码同时干了三件事弹窗提示、标题栏写清错误类型、图标区分错误级别。答辩时老师看的是你对异常路径的处理登录成功路径谁都会写失败路径才是拉开差距的地方。4. 学生信息增删改查PreparedStatement 与 Swing 表格的数据映射4.1 新增学生参数化插入与唯一键冲突处理新增学生是整个系统最核心的业务动作。界面上是JTextField控件收集输入提交时调用 DAO 层把数据写进student表。这里有一个控件台项目最常见的架构误区把 JDBC 代码直接写进窗口类的按钮监听器里。一个按钮回调里同时出现getConnection、PreparedStatement、JOptionPane和界面刷新逻辑短期看能跑但后续加「修改」「删除」「查询」时同一个类会膨胀到难以维护。我通常的做法是独立一个StudentDAO类界面只负责收集数据并调用dao.insert(student)DAO 返回布尔值表示是否成功。public boolean insert(Student student) { String sql INSERT INTO student (student_no, name, gender, age, class_name, phone) VALUES (?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, student.getStudentNo()); ps.setString(2, student.getName()); ps.setInt(3, student.getGender()); ps.setInt(4, student.getAge()); ps.setString(5, student.getClassName()); ps.setString(6, student.getPhone()); return ps.executeUpdate() 0; } catch (SQLIntegrityConstraintViolationException e) { System.err.println(学号重复: student.getStudentNo()); return false; } catch (SQLException e) { e.printStackTrace(); return false; } }这里要特别说明executeUpdate()的返回值含义它返回受影响的记录行数插入一条数据成功应该是 1所以 0判断没问题。SQLIntegrityConstraintViolationException是 JDBC 规范里的标准异常当student_no触发了数据库的UNIQUE约束时抛出。你可能会问为什么不在插入前先查一次学号是否存在我的建议是两者都做应用层先查一次是为了给出友好的「学号已存在」提示数据库约束是兜底防止并发场景下两条 insert 同时通过预检查。课设阶段做一次预检查就够但你要在代码注释里写明这一层考虑答辩时都是加分项。界面上性别通常用JRadioButton控件取值时要判断isSelected()而不是拿getText()去匹配「男/女」因为按钮的文本可能被修改但getActionCommand()对应的值不该变。我习惯给两个单选按钮分别设置setActionCommand(1)和setActionCommand(0)提交时通过ButtonGroup获取选中的 actionCommand 转成 int。这个套路在后续的「按性别筛选」功能里也能复用。4.2 修改与删除主键定位行不要用学号修改和删除的核心不是 SQL 本身而是「用哪一列定位记录」。很多人的第一反应是用student_no学号做WHERE条件因为业务上它是唯一的。但在实际的表设计里主键是id自增 int它的职责就是定位物理行student_no虽然也有唯一约束但它本质是业务标识。当你要做「把学号从 20240101 改成 20240102」时用student_no做WHERE就尴尬了——你还没改条件就已经失效了。public boolean update(Student student) { String sql UPDATE student SET student_no?, name?, gender?, age?, class_name?, phone? WHERE id?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, student.getStudentNo()); ps.setString(2, student.getName()); ps.setInt(3, student.getGender()); ps.setInt(4, student.getAge()); ps.setString(5, student.getClassName()); ps.setString(6, student.getPhone()); ps.setInt(7, student.getId()); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }注意WHERE id?这个条件的顺序占位符索引 7 对应的id参数排在最后和 SQL 里?的出现顺序严格一致。这个顺序错位是PreparedStatement最常见的 bug——SQL 能编译但运行时数据写错列排查起来特别费劲。推荐每次写完先打印 SQL 和参数组合确认再提交。删除功能我只贴核心代码逻辑和 update 对称public boolean deleteById(int id) { String sql DELETE FROM student WHERE id ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setInt(1, id); return ps.executeUpdate() 0; } catch (SQLException e) { e.printStackTrace(); return false; } }删除有个交互层的坑用户可能没选中任何一行就点了删除按钮。界面上要通过JTable.getSelectedRow()判断返回值是否为 -1-1 表示没有任何行被选中。如果不做这个判断直接取model.getValueAt(selectedRow, 0)拼 id会抛出ArrayIndexOutOfBoundsException这就是典型的「界面层没做前置校验锅却甩给 DAO」的翻车现场。4.3 查询与结果回显把 ResultSet 转成 JTable 能认识的数据查询分两种无条件加载全部、按关键字模糊查询。数据量不大时全量加载无所谓但代码里要刻意写成「分页查询」的形态这也是常见的答辩追问点。public ListStudent queryByKeyword(String keyword, int page, int pageSize) { ListStudent list new ArrayList(); StringBuilder sql new StringBuilder(SELECT id, student_no, name, gender, age, class_name, phone FROM student); if (keyword ! null !keyword.trim().isEmpty()) { sql.append( WHERE name LIKE ? OR student_no LIKE ? OR class_name LIKE ?); } sql.append( ORDER BY id DESC LIMIT ? OFFSET ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql.toString())) { int idx 1; if (keyword ! null !keyword.trim().isEmpty()) { String like % keyword.trim() %; ps.setString(idx, like); ps.setString(idx, like); ps.setString(idx, like); } ps.setInt(idx, pageSize); ps.setInt(idx, (page - 1) * pageSize); try (ResultSet rs ps.executeQuery()) { while (rs.next()) { Student stu new Student(); stu.setId(rs.getInt(id)); stu.setStudentNo(rs.getString(student_no)); stu.setName(rs.getString(name)); stu.setGender(rs.getInt(gender)); stu.setAge(rs.getInt(age)); stu.setClassName(rs.getString(class_name)); stu.setPhone(rs.getString(phone)); list.add(stu); } } } catch (SQLException e) { e.printStackTrace(); } return list; }这段代码里有三个关键决策。第一LIKE的匹配串%keyword%放在 java 层拼接好而不是直接写LIKE %?%因为?是占位符不能放进字符串字面量里。第二LIMIT ? OFFSET ?这两个参数也是占位符但要注意它们的类型pageSize是每页条数OFFSET是跳过的行数等于(page - 1) * pageSize这是分页的标准公式。第三从ResultSet取列值时用列名而不是列索引例如rs.getString(student_no)。虽然列索引性能略好但列名可读性强SQL 里列的排列顺序一变不至于全盘炸掉。把ListStudent渲染到JTable上的标准写法是DefaultTableModel。注意每次刷新数据时要先setRowCount(0)清空旧数据再逐行addRow否则上一次查询结果会残留在表格里越点查询数据越多。DefaultTableModel model (DefaultTableModel) table.getModel(); model.setRowCount(0); for (Student stu : list) { model.addRow(new Object[]{ stu.getId(), stu.getStudentNo(), stu.getName(), stu.getGender() 1 ? 男 : 女, stu.getAge(), stu.getClassName(), stu.getPhone() }); }这里还有一个小细节gender字段在数据库里是tinyint控件台表格里直接显示 1/0 不友好用三元表达式转成「男/女」。这种转换放在界面层而不是 SQL 层是为了保持 DAO 层返回的数据和表结构一致界面只做展示适配。5. 学生信息管理系统的 4 个高频踩坑点从 SSL 报错到中文乱码5.1 连接层的三个经典报错SSL、时区与驱动类名第一个高频报错是Establishing SSL connection without servers verification。现象控制台打出大段红色警告连接最后其实成功了但心里没底。原因MySQL 服务端默认开了 SSL 支持而 JDBC 驱动默认也要协商 SSL本地开发没有证书就会告警。解决URL 里显式加useSSLfalse明确告诉驱动不需要加密通道。注意这个告警在 MySQL 5.7 上是警告不阻断在个别版本上会直接升级成SSL connection error所以别赌它不报错直接加上更省事。第二个报错是The server time zone value Öйú±ê׼ʱ¼ä is unrecognized。现象看到一堆乱码时区名然后连接直接失败。原因MySQL 8.0 的驱动对时区敏感而系统默认时区和驱动解析机制对不上。解决在连接串尾部追加serverTimezoneAsia/Shanghai如果还不行把时区改成GMT%2B8即 GMT8 的 URL 编码形态。记住一个优先级serverTimezoneAsia/Shanghai是最稳妥的写法GMT8在有特殊字符环境下反而容易出解析问题。第三个报错是ClassNotFoundException: com.mysql.cj.jdbc.Driver。现象代码完全按教程抄的编译也过了一运行就找不到类。原因你的 MySQL 是 8.0但教程里的驱动类是 5.x 的com.mysql.jdbc.Driver或者反过来你项目里引的是 5.x 驱动却写了 8.x 的类名。解决先确认mysql-connector的 jar 版本再写对应的Class.forName。我用过最笨但最有效的方法解压 jar 进去直接看META-INF/services/java.sql.Driver文件里写的是哪个类名用那个类名就不会错。5.2 数据层的四个翻车现场服务不启动、端口占用、权限拦截与中文乱码第一个现场是Communications link failure。现象报错信息里带着Connection refused看起来像是代码问题。原因MySQL 服务没启动或者端口被改过。解决先用netstat -ano | findstr 3306看端口有没有进程监听没监听就去 Windows 服务里手动启动MySQL服务有监听但连不上大概率是my.ini里改了port把连接串里的 3306 改掉。这一步很多人绕了远路去检查代码其实最快的排查路径是「先确认服务再确认端口再确认账号密码」。第二个现场是Access denied for user rootlocalhost。现象用户名密码确定没错但就是被拒。原因root 的密码强度策略或者认证插件和你的客户端不匹配尤其是 MySQL 8.0 的caching_sha2_password在旧版 Navicat 或旧版驱动下会握手失败。解决Navicat 升级到 16 以上驱动升到 8.x如果只是本地学习不想折腾认证插件可以执行ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码;把 root 切回老认证方式。这不是生产环境的推荐做法但课设阶段能救命。第三个现场是Unknown column xxx in field list。现象SQL 看起来没问题但跑起来说列不存在。原因你有两个数据库代码连的库和你在 Navicat 里建表的库不是同一个。解决在DBUtil的 URL 里把数据库名打印出来核对一次另外在 SQL 工作台执行SELECT DATABASE();确认当前连接选中的是哪个库。这类问题最坑的地方在于它是「半正确」的——服务能连、驱动能跑、账号密码都对就是库名错了。第四个现场是插入中文后显示???。现象Navicat 里看数据全是问号但代码没报错。原因连接串里没有characterEncodingutf8或者建表时字符集不是utf8mb4。解决建表统一用utf8mb4连接串统一带useUnicodetruecharacterEncodingutf8双保险。还要检查代码文件本身的编码IDEA 右下角把文件编码从 GBK 切到 UTF-8否则源码里的中文字符串字面量在编译时就已经乱了这属于「字符集层层传递」里最容易忽略的一环。提示以上四个现场是按出现频率排的其中「服务不启动」和「中文问号」最让人心态崩溃因为它们往往不是代码 bug而是环境状态问题。遇到先深呼吸然后按「服务 → 端口 → 库名 → 字符集 → 驱动类名」五步排查法走一遍90% 的问题十分钟内能定位。5.3 控件台界面层最容易拖垮进度的两个问题第一个是JTable的表头不刷新。现象第一次加载有表头执行一次查询后表头消失了。原因你重新setModel的时候把带表头的DefaultTableModel换成了不带表头的新模型或者直接对JTable的 model 做了强转但模型类型不匹配。解决不要在查询方法里新建DefaultTableModel后再setModel而是getModel拿到原有模型后操作它的数据。表头是JTable的getTableHeader()管理的一旦模型被替换表头里的列名全部丢失。第二个是窗口尺寸和控件布局超出可视区。现象在小屏幕笔记本上跑按钮和输入框被截断看不到「新增」「保存」按钮。解决主窗口setSize(1000, 600)起步JFrame默认布局是BorderLayout中间放JScrollPane包住的表格底部放JPanel包住按钮不要用绝对定位setLayout(null)。绝对定位在课设里很常见但它换来的是「换台电脑就乱套」的糟糕体验答辩演示时如果窗口被截断印象分会大打折扣。6. 把系统做成能拿得出手的样子批量导入、表格刷新与演示录像的验证顺序6.1 用批处理把批量录入从「慢动作」变成「秒级」如果演示时需要一次录入几十条学生数据千万不要用 for 循环逐条调用dao.insert()。每次insert都是一次完整的网络往返加事务提交几十条数据会明显卡顿。更优的做法是用 JDBC 的批处理先addBatch()攒着再一次executeBatch()交出去。public boolean batchInsert(ListStudent students) { String sql INSERT INTO student (student_no, name, gender, age, class_name, phone) VALUES (?, ?, ?, ?, ?, ?); try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { for (Student s : students) { ps.setString(1, s.getStudentNo()); ps.setString(2, s.getName()); ps.setInt(3, s.getGender()); ps.setInt(4, s.getAge()); ps.setString(5, s.getClassName()); ps.setString(6, s.getPhone()); ps.addBatch(); } int[] results ps.executeBatch(); return results.length students.size(); } catch (SQLException e) { e.printStackTrace(); return false; } }executeBatch()返回的int[]数组每个元素对应一条 SQL 的影响行数。如果有一条失败默认行为是中断并抛出BatchUpdateException前面的成功数据不会自动回滚。所以在真实场景里批处理必须配合事务手动控制conn.setAutoCommit(false)、执行批处理、确认全部成功后再conn.commit()任一条失败就conn.rollback()。课设阶段可以不写完整事务但注释里要写清楚这个缺口——面试问「批处理失败怎么保证一致性」时你能答出「需要外层事务包裹」就说明真懂。6.2 演示视频的录制顺序按老师最容易追问的路径走标题里写了「含演示视频」这个视频的录制顺序直接决定答辩观感。我建议按五步走第一步演示登录故意输错一次密码再输对证明异常处理存在第二步演示新增学生中文、数字、边界年龄都输一遍然后到数据库里查这条记录证明真的落库了第三步演示查询用关键字模糊搜刚才录入的名字第四步演示修改与删除改完刷新表格看变化第五步重启系统再次登录并查询证明数据持久化成功。这个顺序的隐藏逻辑是「每一步的输入都能在后一步看到结果」形成闭环。视频录制时注意避开两个细节一是窗口不要最大化后再录保持默认尺寸避免变形二是输入密码时不会被录屏软件记录明文但你要提前在代码里确认密码字段用的是JPasswordField而不是JTextField否则视频里密码明文会直接展示出来答辩现场也容易被老师当场指出来。6.3 验收自测清单能跑通不等于能交差最后给自己留一个十分钟的自测清单比反复「点着玩」更有效第一条关掉 MySQL 服务再启动系统看是否报错且报错信息是否友好至少不能是英文堆栈直接甩到控制台第二条连续点击「保存」按钮两次看学号重复拦截是否生效第三条把系统窗口缩到最小再还原看控件布局是否错乱第四条在查询框输入单引号或百分号%看是否报 SQL 异常——这也是 SQL 注入压力测试的最低版本第五条用Navicat手动删掉一条数据回系统点刷新看界面的空数据状态是否能用。这个项目的技术深度不算高但它是少数几个能让你把「面向对象」「JDBC 规范」「数据库约束」「异常处理」四条线完整串起来的练习。我当年做课设时图省事把密码明文放在代码里连接串里连useSSL都不写运行起来一堆黄字也懒得管结果答辩演示时换了台电脑直接连不上数据库当场翻车。后来我把这套系统的连接参数和自测清单沉淀成了固定模板换任何机器都能在十分钟内把它跑起来。希望这篇笔记能帮你少走这一步弯路也希望你做完后能把它跑通在「不熟悉的机器」上——那才是真的做完了。本文还有配套的精品资源点击获取
返回列表