Java+Swing+MySQL教务管理系统开发实战与避坑指南
发布时间:2026/10/9 14:44:16 锦皓数字建站

简介这是一套基于Java Swing与MySQL开发的学校教务管理系统完整源码定位为高校Java课程设计、期末大作业及毕业设计备题资源难度适中且已通过助教老师审定。项目涵盖学生信息管理、教师信息管理、课程安排、成绩录入与查询、登录鉴权等常见教务业务模块界面采用Swing桌面组件构建数据层通过JDBC连接MySQL附带SQL初始化脚本与Maven工程配置有助于理解业务逻辑、界面事件处理、数据库表设计及客户端与数据库交互流程。压缩包共包含39个文件以25个Java源文件为主另有6张界面运行截图、5个XML配置文件、1个SQL脚本、1个README说明文档及.gitignore等整体大小仅240KB结构清晰紧凑便于导入IDE直接运行和学习。源码已经本地编译验证可稳定运行评审分达到98分代码结构与命名规范具有一定示范性。目前已有220人学习下载适合需要快速完成期末大作业或课程设计的Java初学者参考借鉴。1. JavaSwingMySQL教务管理系统课程设计里最实在的一块拼图JavaSwingMySQL教务管理系统是 Java 期末大作业里被点名最多的一道题也是课程设计里的默认组合Swing 画界面JDBC 连 MySQLMaven 管依赖和构建。这份资源我完整跑过一遍pom.xml、educational_admin.sql、src 和 test 都在导入 IDEA 改好连接就能启动。场景很直观登录区分管理员、教师、学生三类角色之后分别维护学生信息、课程、选课和成绩。功能不算惊艳但面向对象、JDBC 操作、Swing 事件模型全覆盖交作业足够了。适合三类人现在要交作业的、想照着 SwingMySQL 完整例子复现的、以及拿到源码不知道怎么讲设计结构的。但 target 里已有 classes 不代表你能直接跑不同 JDK/MySQL 版本会把“可运行”变成“环境不对”我建议先看完下面的目录和脚本再动手。2. 先读懂项目骨架pom.xml、educational_admin.sql 与 Swing 分层结构拿到压缩包先别急着点运行。我的习惯是先看目录再读 SQL 脚本最后才打开 IDEA。这个顺序能帮你三分钟判断资源完整度也能避开很多后面才爆出来的环境问题。2.1 目录结构Maven 布局里哪些是重点先从 zip 里解出来的根目录开始看正常的 Maven 工程长这样educational_admin/ ├── pom.xml ├── educational_admin.sql ├── README.md ├── .gitignore ├── src/ │ ├── main/ │ │ ├── java/ │ │ └── resources/ │ └── test/ │ └── java/ ├── target/ │ └── classes/先说结论pom.xml 是整个项目的依赖和打包配置核心educational_admin.sql 是数据库初始化脚本src/main/java 里是业务代码src/test 用来放单元测试target/classes 是上一次编译的产物。资源里出现 target 和 classes说明作者是在本机成功编译过才打包的这一点比很多只丢源码的压缩包靠谱。再看 .gitignore。它在这里的作用不是运行而是告诉你作者是用 Git 管理版本的里面通常忽略了 target、.idea、*.iml 这些本地文件。你在这个基础上继续用 Git 维护课程设计就不用自己重新补 ignore 规则。至于压缩包根目录常见的 -master 后缀只是从 Git 平台打 zip 时留下的分支名不影响项目内容。打开 pom.xml 后我一般先看 dependencies 里有没有 mysql-connector-java。纯 Swing JDBC 的课程设计通常只依赖这一个第三方库如果里面还出现了 JFreeChart、POI 之类的说明还有导出图表或 Excel 的功能。你先记下依赖列表后面避坑章节会用到。dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId !-- 版本号以你本地 Maven 实际拉到的最新版为准 -- /dependency注意这个依赖的版本直接决定了驱动类名5.x 是 com.mysql.jdbc.Driver8.x 是 com.mysql.cj.jdbc.Driver。这看起来是小问题但它是课程设计项目里最常见的启动报错来源之一后面会展开。pom.xml 里如果看不到版本号可以把鼠标放在 IDEA 的 dependency 上按住 Ctrl 查看实际解析到的版本或者用 mvn dependency:tree 输出。2.2 educational_admin.sql表结构里藏着业务边界建库脚本是整个项目的“定盘星”。界面上的每个按钮本质上都在对这个脚本里的表做增删改查所以一定要先读一遍。教务管理系统常规的表就那几类账号表、学生表、教师表、课程表、选课记录表、成绩表。资源的命名可能叫 user 也可能叫 t_user前缀不重要字段逻辑基本逃不出下面这些DROP TABLE IF EXISTS t_user; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(50) NOT NULL COMMENT 密码, role VARCHAR(10) NOT NULL COMMENT admin/teacher/student ); DROP TABLE IF EXISTS t_student; CREATE TABLE t_student ( id INT PRIMARY KEY AUTO_INCREMENT, student_no VARCHAR(20) NOT NULL, name VARCHAR(50) NOT NULL, major VARCHAR(50), class_name VARCHAR(50) ); DROP TABLE IF EXISTS t_course; CREATE TABLE t_course ( id INT PRIMARY KEY AUTO_INCREMENT, course_name VARCHAR(100) NOT NULL, teacher_id INT, max_count INT DEFAULT 50, selected_count INT DEFAULT 0 ); DROP TABLE IF EXISTS t_selection; CREATE TABLE t_selection ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT, course_id INT, score DECIMAL(5,2) DEFAULT NULL );执行这些脚本后你会发现整个系统的业务边界也清楚了学生通过 t_selection 和 t_course 关联成绩挂在选课记录上教师通过 teacher_id 关联课程。这就是“学生选课、教师录成绩、管理员维护基础数据”闭环的物理基础。脚本里通常还有初始数据最常见的是给每个角色预设一个账号。没有初始账号的话登录页就是空转你导入后必须先手工插一条管理员否则永远登不进去。我的做法是先执行下面的查询确认脚本里到底有没有账号SELECT * FROM t_user;如果结果是空表自己补一条最低限度的管理员即可密码字段先在后台用已知的明文跑通后再考虑加密。这里先不展开加密最后一章会讲。2.3 Swing 分层View、Service、DAO 谁管什么很多课程设计项目的真实形态是“类都写在一个 JFrame 里”按钮一多一个类能有两千行。能跑但很难讲也难改。合理的结构是三层Swing 窗体只负责接收鼠标键盘事件和展示数据Service 层处理登录校验、选课资格这类业务规则DAO 层只写 JDBC不掺界面逻辑。我先给一个 DAO 层的典型写法你看完后可以对照资源里的包名去定位public class UserDao { public User findByUsernameAndPassword(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 user new User(); user.setId(rs.getInt(id)); user.setUsername(rs.getString(username)); user.setRole(rs.getString(role)); return user; } } } catch (SQLException e) { e.printStackTrace(); } return null; } }这里我特意用了 PreparedStatement 而不是 Statement因为用字符串拼接写出来的 SQL 在答辩时被问到“如何防 SQL 注入”会很被动。参数用问号占位再通过 setString 传值既安全又不用处理引号转义。连接从 DBUtil 统一获取正常情况下它会读一个 db.properties 文件。最低成本的写法是直接放在一个常量类里但为了不改代码就能换数据库我更推荐用 properties 文件jdbc.urljdbc:mysql://localhost:3306/educational_admin?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password你的密码注意 URL 里的 serverTimezone 参数MySQL 8.x 驱动不写这个十有八九会报时区异常。后面的避坑章节会专门再说。三层拆分之后JFrame 里就不需要知道 SQL 怎么写了它只调 ServiceService 再调 DAO边界清清楚楚。答辩时老师问“你这个功能怎么实现的”你就能按这条链路讲下来而不是翻几百行代码找按钮。3. 核心代码拆解登录校验、学生管理、选课事务的 JDBC 写法这一章专门拆三个最常出问题的功能点登录、表格刷新、事务。这三个点懂了剩下都是重复动作。3.1 登录事件转发与角色跳转Swing 登录界面本质上是把 JTextField 和 JPasswordField 里拿到的值交给 Service 层去数据库比对然后根据角色打开不同的主窗口。按钮事件里的代码如下JButton loginBtn new JButton(登录); loginBtn.addActionListener(e - { String username usernameField.getText().trim(); String password new String(passwordField.getPassword()); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(frame, 用户名和密码不能为空); return; } User user new UserService().login(username, password); if (user ! null) { frame.dispose(); if (admin.equals(user.getRole())) { new AdminMainFrame().setVisible(true); } else if (teacher.equals(user.getRole())) { new TeacherMainFrame().setVisible(true); } else { new StudentMainFrame(user).setVisible(true); } } else { JOptionPane.showMessageDialog(frame, 用户名或密码错误); } });这里有两个容易被忽视的点。第一密码框要用 getPassword() 而不是 getText()因为它返回 char[] 而不是 String这是 Java 在密码处理上默认的安全习惯。第二登录成功后要用 frame.dispose() 把登录窗口销毁而不是 setVisible(false)否则进程里挂着隐藏窗口内存上没必要。UserService.login 内部就是查 t_user 表比对成功后返回一个带 role 的 User 对象。如果资源里没有 Service 层直接在事件里写 JDBC也能跑但建议你自己拆一层出来因为这正好是答辩可以讲的设计点界面层不知道 SQL数据层不知道按钮。3.2 学生管理JTable 刷新模型别追加旧数据教务系统里学生列表几乎都用 JTable DefaultTableModel。经典翻车现场是每次点“查询”都在往表格里 addRow但是旧数据还留在 model 里结果越点越多。正确的刷新逻辑是先清空再填充private void loadStudentData() { String sql SELECT id, student_no, name, major FROM t_student ORDER BY id; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { DefaultTableModel model (DefaultTableModel) studentTable.getModel(); model.setRowCount(0); while (rs.next()) { model.addRow(new Object[]{ rs.getInt(id), rs.getString(student_no), rs.getString(name), rs.getString(major) }); } } catch (SQLException ex) { JOptionPane.showMessageDialog(this, 查询失败 ex.getMessage(), 错误, JOptionPane.ERROR_MESSAGE); } }setRowCount(0) 是清空旧数据的关键缺了它第二次查询开始表格就会出现重复。如果你在初始化表格时用的是这种写法studentTable.setModel(new DefaultTableModel( new Object[]{ID, 学号, 姓名, 专业}, 0 ));那么 loadStudentData 里就可以直接强转 DefaultTableModel不需要每次重设表头。如果资源里表格没有正确初始化强制转换会抛 ClassCastException那就先改初始化那行再调刷新。删除操作的处理逻辑是从表格选中行拿到主键执行 delete再重新调用 loadStudentData 刷新界面。int selectedRow studentTable.getSelectedRow(); if (selectedRow 0) { int id (int) studentTable.getValueAt(selectedRow, 0); new StudentDao().deleteById(id); loadStudentData(); }这里注意 getValueAt 返回的是 Object先转成 Integer 再变成 int。很多同学忘了强转编译直接报错或者因为表格列顺序不对把第二列的学号当成主键删了删出来的数据和 UI 显示完全对不上。所以建议在表格里把主键列放在第一列并在初始化时隐藏它或者单独维护一个 List 来映射行号到主键。3.3 选课与成绩录入JDBC 事务保证数据一致性选课这个动作会同时影响两张表在 t_selection 里插入一条选课记录同时把 t_course 里的 selected_count 加一。如果第一个操作成功、第二个失败选课记录就成了孤儿数据。避免这个问题必须把两个操作放进同一个事务里public boolean chooseCourse(int studentId, int courseId) { Connection conn null; try { conn DBUtil.getConnection(); conn.setAutoCommit(false); String insertSql INSERT INTO t_selection(student_id, course_id, score) VALUES(?,?,NULL); try (PreparedStatement ps conn.prepareStatement(insertSql)) { ps.setInt(1, studentId); ps.setInt(2, courseId); ps.executeUpdate(); } String updateSql UPDATE t_course SET selected_count selected_count 1 WHERE id?; try (PreparedStatement ps conn.prepareStatement(updateSql)) { ps.setInt(1, courseId); ps.executeUpdate(); } conn.commit(); return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } return false; } finally { if (conn ! null) { try { conn.setAutoCommit(true); } catch (SQLException e) { e.printStackTrace(); } DBUtil.close(conn); } } }这段代码有几个细节。第一setAutoCommit(false) 必须在所有操作之前调用否则每条 SQL 都自动提交事务就形同虚设。第二finally 里把 autoCommit 恢复成 true 是个好习惯因为连接要还回池里如果保持 false同一个连接下一次被使用时前一个事务未提交的操作状态会带过来。第三两个 PreparedStatement 分别在 try-with-resources 里自己关闭但 Connection 是在最外层手动关闭因为事务期间不能用 try-with-resources 提前把连接关掉。教师录成绩在原理上更简单本质就是一条 update 语句UPDATE t_selection SET score 8.5 WHERE student_id ? AND course_id ?但如果学生选了多门课而界面只给了学号和分数没给课程号那 update 就会把所有课程成绩一起改掉。这一点很多课程设计项目都会踩建议在成绩录入界面把课程作为必选项或者用 JTable 展示选课记录时直接点击某一行进行编辑减少这种歧义。4. 本地编译运行全流程MySQL 建库、Maven 打包与启动参数前两章是拆架构这一章回到地面怎么把资源里的代码变成你电脑上能看到登录窗的实际运行程序。按顺序做十分钟能跑起来。4.1 MySQL 初始化命令行导入脚本与账号确认如果你的 MySQL 装的是 5.7 或 8.0最稳的导入方式是在命令行里执行mysql -u root -p educational_admin.sql回车后会提示输入密码输入 root 的密码脚本里的建库建表和初始数据就全部执行了。这里注意脚本里如果有 DROP TABLE 语句它会把同名表先删掉再重建所以不要在已经存有自己数据的库上执行先看一眼脚本头部有没有 DROP 字样。导入完成后用下面这条命令验证一下表是否建出来了mysql -u root -p -e USE educational_admin; SHOW TABLES;这一步非常有必要因为很多课程设计项目里 SQL 脚本的字符集、表名可能和 Java 代码里写的不一致。如果 Java 代码里查的是 t_student而 SQL 脚本里建的表叫 student那你代码怎么改都会报“表不存在”。先确认表名再对照代码里的 SQL避免在这个问题上浪费时间。图形化工具比如 Navicat、MySQL Workbench 也可以导入但要注意导入时保持 UTF-8 编码。命令行默认读取脚本文件的编码Windows 下如果你的脚本是 GBK 保存的就会出现乱码。资源里的脚本我建议用记事本或 VSCode 打开把右下角的编码确认成 UTF-8 后再执行。4.2 Maven 编译打包在 IDEA 里还是命令行里跑资源是标准 Maven 工程所以最高效的方式是直接用 IDEA 打开根目录。IDEA 会自动识别 pom.xml开始下载依赖右下角进度条走完基本就准备好了。如果你想用命令行验证这几个命令是最常用的mvn clean compile mvn packageclean 会清掉 target 目录里的旧 class避免新旧代码混杂compile 把 src/main/java 编译成 classpackage 进一步打 jar。对于结构不复杂的 Swing 课程设计执行到 package 就已经能看到 target 目录下生成带依赖或纯项目的 jar 了。但这里有一个非常大的坑普通的 Maven project 打出来的 jar 不包含第三方依赖。也就是说你直接 java -jar xxx.jar 会报 NoClassDefFoundError提示找不到 mysql 驱动类。因为你没有把 mysql-connector-java 这个 jar 打包进去。解决方式是加 maven-shade-plugin把依赖合并进最终 jarplugin groupIdorg.apache.maven.plugins/groupId artifactIdmaven-shade-plugin/artifactId !-- 版本号按你本地 Maven 仓库能拿到的版本填 -- executions execution phasepackage/phase goalsgoalshade/goal/goals /execution /executions configuration transformers transformer implementationorg.apache.maven.plugins.shade.resource.ManifestResourceTransformer mainClasscom.yourpackage.Main/mainClass /transformer /transformers /configuration /pluginmainClass 里的类名要换成资源里真正带 main 方法的启动类一般是 MainFrame 或者 Main。如果你不确定就打开 src/main/java 往下翻找带 public static void main 的类。配置正确后执行 mvn clean packagetarget 下会同时出现原始 jar 和带依赖的 jar带依赖的那个命名一般是 xxx.jar直接双击或者 java -jar 都能跑。如果只是自己交作业我一般只在 IDEA 里点绿色运行按钮不去打包。但如果你要演示给老师看或者想在一台没装 IDEA 的机器上演示shade 插件这一步是绕不过去的。4.3 启动前的三个检查时区、SSL、字符集即使导入成功第一次运行仍可能被 JDBC 连接串卡住。这一节把最常见的三个连接参数一次说清。先看一个配置文件的完整写法jdbc.urljdbc:mysql://localhost:3306/educational_admin?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8 jdbc.usernameroot jdbc.password你的密码useSSLfalse 是在本地开发时推荐的避免 MySQL 8.x 默认启用 SSL 后出现 SSLHandshakeException。serverTimezoneAsia/Shanghai 是为了解决报错 “The server time zone value йʱ is unrecognized” 这个问题本质是驱动和服务器时区不一致。characterEncodingutf8 保证传入数据库的中文不乱码但要注意它只对当前连接有效数据库和表本身的字符集也要是 utf8mb4否则结果集里还是乱码。还有一个参数 allowPublicKeyRetrievaltrue 值得提它经常和 MySQL 8.0 的 caching_sha2_password 插件一起出现。如果连接报错提示 Public Key Retrieval is not allowed就在 URL 后面追加这个参数。这三个参数都不算稀奇但放一起能解决 80% 的“MySQL 连接不上”问题。启动前最后检查一件事如果你在 Windows 命令行跑MySQL 服务默认不会自动启动。用管理员身份打开命令提示符输入 net start mysql或者在服务管理器里启动 MySQL。这个说起来有点基础但课程设计验收现场十个卡死跳过的项目里有四五个是服务没开。5. 避坑指南SwingMySQL 项目最常见的五个坑与排查路径这一章是血泪总结。每一个坑都是我亲眼见人踩过其中一半自己也踩过按“现象 → 原因 → 解决”写你可以直接对照着排查。5.1 驱动类找不到ClassNotFoundException 但 pom 里明明有依赖现象启动后控制台第一行报 java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver然后程序退出。原因要么依赖没有真正导入要么你用的是 MySQL Connector/J 5.x驱动类名是 com.mysql.jdbc.Driver而 8.x 驱动才叫 com.mysql.cj.jdbc.Driver。代码里 Class.forName 写反了加载就会失败。解决先看 pom.xml 里 mysql-connector-java 的版本。如果是 8.x把 Class.forName 的值改成 com.mysql.cj.jdbc.Driver如果是 5.1.x改回 com.mysql.jdbc.Driver。同时确认 IDEA 的 Maven 面板没有红色依赖如果依赖报错就执行右侧的 Reload All Maven Projects。最内部的排查命令是mvn dependency:tree只要能列出 mysql-connector-java 节点就说明依赖解析到了。如果依赖在 pom 里不在那问题出在 IDEA 的本地仓库索引删除项目里的 .idea 文件夹重新导入一次就好。5.2 中文乱码界面、数据库、连接三个地方要一致现象学生名单里的中文在界面上显示成问号或者数据库里表数据正常但 JTable 里全是 。原因字符集链路有三层界面 Java 文件编码、JDBC 连接参数、MySQL 库表和客户端编码只要有一层是 GBK 而其他层是 UTF-8中文就会烂。最常见的是连接 URL 里没写 characterEncodingutf8或者建库脚本里用了 DEFAULT CHARSETlatin1。解决建库统一用 utf8mb4URL 里追加 characterEncodingutf8IDEA 里再检查一下 File Encoding 设置保证项目编译器输出都是 UTF-8。如果是 Windows 控制台运行看到中文乱码在运行时加 JVM 参数 -Dfile.encodingUTF-8。这三处一致了乱码基本消失。如果还存在问题去查 MySQL 变量的 global character_set_server 是不是 utf8mb4。5.3 界面卡死Swing 的 EDT 线程里跑了 JDBC现象点“查询”按钮之后整个窗口拖不动鼠标一直转圈十几秒后才有响应或者干脆假死。原因Swing 是单线程模型所有界面刷新必须在事件分发线程 EDT 上执行。如果你在按钮事件里直接发起数据库查询JDBC 等待响应时 EDT 被堵死界面自然不动。解决用 SwingWorker 包一层把耗时查询放到后台线程完成后回到 EDT 更新界面。一个典型的修复模板是这样new SwingWorkerVoid, Void() { Override protected Void doInBackground() { dao.queryAll(); // 放耗时查询 return null; } Override protected void done() { loadStudentData(); // 回到界面线程刷新 } }.execute();注意 doInBackground 里不能碰任何 Swing 组件否则仍然有线程安全问题。把查询结果先存在类成员变量里done 方法里再读取并填充 JTable。课程设计项目数据量小很多人不写这一步也能跑但一旦老师录入了上千条数据卡一次答辩印象分就低了。5.4 JTable 表格不显示数据或表头错位现象查询返回了结果数据库里也有数据但表格空荡荡或者数据出现但表头还是默认的 A、B、C。原因表格还没有初始化 TableModel 时你就去 setRowCount 或 addRow摸了个空模型或者是强转 DefaultTableModel 失败。另一个原因是 ResultSet 里的列名和表头不映射你 getString(student_no) 里面名字写错异常被 catch 吞掉导致界面毫无反馈。解决在窗体构造方法里先把表格模型定死studentTable.setModel(new DefaultTableModel( new Object[]{ID, 学号, 姓名, 专业}, 0 ));然后每次查询都基于这个固定模型往里面加行。如果 catch 到 SQLException别只 e.printStackTrace加一个 JOptionPane 把错误信息弹出来否则你死活不知道数据为什么没进来。顺带说一句ResultSet 里的列名是大小写不敏感的但下划线和大小写风格要和 SQL 里 select 出来的别名对齐最简单的方式是 select id, student_no 而不是 select *。5.5 打包后 NoClassDefFoundErrorjar 能运行但不能碰 MySQL现象IDEA 里运行一切正常但双击 jar 包报错NoClassDefFoundError 指向 com/mysql/cj/jdbc/Driver或者提示类路径里找不到某个包。原因默认的 maven-jar-plugin 只打包项目自己的类不包含任何第三方依赖。你的 IDE 运行环境里有 mysql-connector-java但 jar 跑起来后就没有这个依赖了。另一个可能是 MANIFEST.MF 里没写 Main-Class导致双击时提示无法加载主类。解决按第四章给 maven-shade-plugin 的配置在 pom.xml 里加上插件且指定 mainClass再执行 mvn clean package。打包成功后用这个命令验证一下 jar 里的 MANIFEST 是否有主类java -jar target/your-project.jar如果还不行用 jar tf 查看是否能看到 com/mysql/cj 相关 class。看不到就是 shade 配置没生效检查 executions 是否挂在 package 阶段以及插件版本是否被成功解析。从这个坑往后我拿到任何 Maven Swing 项目都会先看一眼 pom 有没有打包插件没有就先补上再谈别的。6. 答辩前这样升级密码加密、预编译、连接池与发行包想要从“能跑”变成“能讲”不用大改功能做对三个细节就够了。第一个是密码存储。资源初始账号里如果 password 还是明文 123456答辩时老师第一个问题大概率是“密码怎么防泄露”。改成哈希存储登录校验也同步改成哈希比对是很加分的改动MessageDigest md MessageDigest.getInstance(SHA-256); byte[] digest md.digest(password.getBytes(StandardCharsets.UTF_8)); String hashed HexFormat.of().formatHex(digest);登录时用相同逻辑对输入做哈希再拿哈希值去查库。如果资源里的登录是直接 SELECT ... WHERE password?你可以先跑通再把它替换成哈希版本。要更严谨还可以加盐但加盐需要在库里额外存一列课程设计做到哈希这步就足够说明你有安全意识了。第二个改动是统一走 PreparedStatement。我见过不少源码里用 Statement 字符串拼接写查询虽然能跑但属于答辩现场的高危雷区。如果你在资源里发现这类写法不用全部重写把最核心的登录和查询改成预编译然后准备一句话“这里用占位符防止 SQL 注入对用户输入不做字符串拼接”。这句话在面试题里也常被问到背下它比背一行代码更值。第三个改动是连接管理。很多简单项目每次查库都 DriverManager.getConnection操作完了关闭。答辩时你可以主动提连接池这个概念最低成本的演示是引入 HikariCPHikariConfig config new HikariConfig(); config.setJdbcUrl(URL); config.setUsername(USER); config.setPassword(PASSWORD); config.setMaximumPoolSize(10); HikariDataSource ds new HikariDataSource(config); Connection conn ds.getConnection();注意这需要 pom.xml 里加 HikariCP 依赖如果不想引入第三方库也可以用 DBCP。关键是让老师看到你理解“频繁创建连接很浪费”这件事。这三个改动做完项目的代码量不会增加多少但答辩能从“我做了个系统”变成“我考虑了安全、性能和扩展性”。我做过最蠢的一件事是拿到一份课程设计源码后连 MySQL 服务都没启动就急着点运行报错后以为是源码坏了来回折腾了一小时。从那以后每次拿到源码我都会强制走一遍建库 → 改连接 → 编译 → 运行的流程再开始动手改界面。希望帮到你。本文还有配套的精品资源点击获取
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。