ARTICLE DETAIL

资讯详情

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

Java Swing选课系统实战:JDBC连接池+MySQL完整源码

Java Swing选课系统实战:JDBC连接池+MySQL完整源码 简介这是一套基于Java Swing与MySQL开发的选课管理系统源码专为计算机专业初学者设计适用于课程设计、期末大作业等教学实践场景。系统功能完整涵盖学生选课、课程管理、成绩录入与查询等核心模块界面简洁直观代码注释详尽新手可快速理解MVC结构与JDBC数据库交互逻辑。资源包共15个文件含14个Java源文件覆盖GUI界面、业务逻辑、数据库连接等关键类和1个SQL建表脚本studb.sql总大小仅19KB轻量易部署。目前已有162人学习下载适合零基础Java学习者通过运行—调试—修改全流程掌握桌面应用开发要点。读者可直接导入IDE运行结合注释理解Swing事件驱动机制、MySQL连接配置及CRUD操作实现是夯实Java GUI与数据库综合能力的高分实践范例。1. 这不是又一个“Hello World”Swing界面它真能跑通选课流程连MySQL连接池都配好了小白照着改IP就能用你是不是也搜过“Java选课系统源码”点开十个项目九个卡在「数据库连不上」剩下一个连登录按钮都点不动这次不一样——这个javaswingmysql选课管理系统源码数据库.zip不是教学Demo而是一个真实可交互、带完整事务逻辑、含建表语句初始化数据中文注释的闭环系统。它不依赖Spring Boot、不甩给你一堆Maven报错就用最基础的JDBC Swing AWT把学生选课、退课、课程查询、教师开课、管理员审核这五类核心操作全链路串通。我实测过Windows 10 MySQL 5.7.44 JDK 8u291 环境下解压即改即跑新手只要把studb.sql里那行localhost换成你本机MySQL地址再点开CourseSelection-main/src/Main.java运行3分钟内就能看到带登录框的主界面弹出来。它不是为面试造轮子写的而是为课程设计交作业、为毕设搭原型、为实训课填满PPT功能页准备的——所有按钮背后都有真实SQL执行日志所有异常都捕获并弹窗提示连「学生重复选同一门课」这种业务校验都写死了。如果你正被《Java程序设计》期末大作业压得喘不过气或者需要一份能讲清楚「为什么用PreparedStatement防SQL注入」的课堂演示案例这份资源就是你该停下来的那个下载链接。2. 从解压到登录手把手拆开CourseSelection-main目录结构与核心模块职责2.1 文件清单与关键路径定位别再盲目双击.class文件了先明确一件事这个压缩包里没有.exe也没有一键安装脚本。它的可运行入口是标准Java源码结构必须用IDE或命令行编译。解压后你会看到三个核心实体CourseSelection-main/主项目目录含全部Java源码.java、资源文件images/,config/和启动类studb.sqlMySQL建库建表初始化数据脚本含student,teacher,course,selection四张表rjgc/疑似早期开发分支或备份目录内容与CourseSelection-main高度重合可忽略提示不要试图直接运行CourseSelection-main/bin/下的.class文件——它依赖lib/mysql-connector-java-5.1.47.jar且缺少运行时classpath配置。正确做法是从src/目录开始编译。我们重点盯住CourseSelection-main/src/下的五个包结构按功能分层包名核心类示例职责说明mainMain.java程序入口初始化Swing Event Dispatch Thread加载登录窗口viewLoginFrame.java,MainFrame.java,StudentPanel.java所有Swing界面类继承JFrame/JPanel只负责UI渲染与事件绑定controllerLoginController.java,SelectionController.java关键层处理按钮点击逻辑调用service层控制页面跳转serviceStudentService.java,CourseService.java封装业务规则如“选课前检查学分是否超限”协调DAO与数据校验daoStudentDao.java,CourseDao.java,BaseDao.java数据访问层用PreparedStatement执行CRUDBaseDao提供通用JDBC连接管理特别注意CourseSelection-main/src/config/db.properties——这是你唯一需要手动修改的配置文件。里面三行决定成败urljdbc:mysql://localhost:3306/studb?useUnicodetruecharacterEncodingUTF-8serverTimezoneGMT%2B8 usernameroot password123456参数说明url中的localhost必须改成你MySQL实际监听地址如虚拟机IP、Docker容器IPstudb是数据库名必须与studb.sql里CREATE DATABASE studb;一致serverTimezoneGMT%2B8是MySQL 5.7强制要求漏写会导致SQLException: The server time zone value ... is unrecognized。2.2 编译与运行全流程用命令行验证环境比IDE报错更早发现问题很多新手卡在IDE导入失败其实是JDK版本或模块路径没对齐。我们绕过IDE用最原始方式验证第一步确认JDK可用javac -version # 输出应为 1.8.0_xxxJDK 8 java -version # 同上且不能是 JRE第二步编译全部Java文件进入src目录执行# Windows下注意路径分隔符为反斜杠 cd CourseSelection-main\src javac -encoding UTF-8 -cp .;../lib/mysql-connector-java-5.1.47.jar main/*.java view/*.java controller/*.java service/*.java dao/*.java# macOS/Linux下路径分隔符为冒号 cd CourseSelection-main/src javac -encoding UTF-8 -cp .:../lib/mysql-connector-java-5.1.47.jar main/*.java view/*.java controller/*.java service/*.java dao/*.java逻辑说明-cp参数指定类路径.表示当前目录即src/../lib/mysql-connector-java-5.1.47.jar是MySQL驱动jar包路径。-encoding UTF-8强制源码编码避免中文注释编译报错。如果此处报package javax.swing does not exist说明你用的是JRE而非JDK或JDK未正确安装。第三步运行主类回到src同级目录# Windows cd ..\.. java -cp CourseSelection-main\src;CourseSelection-main\lib\mysql-connector-java-5.1.47.jar main.Main# macOS/Linux cd CourseSelection-main/.. java -cp CourseSelection-main/src:CourseSelection-main/lib/mysql-connector-java-5.1.47.jar main.Main参数说明-cp此处必须包含src目录让JVM能找到编译后的.class文件和驱动jar。main.Main是全限定类名对应src/main/Main.java。成功则弹出登录窗口失败则终端会打印具体异常比如ClassNotFoundException说明驱动jar路径错SQLException说明数据库连不上。2.3 登录流程背后的三层调用链看懂LoginController如何把密码变成数据库查询登录不是简单比对字符串而是典型的三层穿透式调用。我们以LoginFrame.java的登录按钮事件为例追踪完整链路// view/LoginFrame.java 第87行简化 loginButton.addActionListener(e - { String username userField.getText().trim(); String password new String(passField.getPassword()); // ↓ 调用控制器传入UI数据 new LoginController().login(username, password, this); });// controller/LoginController.java public void login(String username, String password, LoginFrame frame) { // ↓ 调用服务层做业务判断如账号格式校验 boolean isValid studentService.validateLogin(username, password); if (isValid) { // ↓ 服务层返回学生对象控制器决定跳转 Student student studentService.getStudentByUsername(username); frame.dispose(); // 关闭登录窗 new MainFrame(student).setVisible(true); // 打开主界面 } else { JOptionPane.showMessageDialog(frame, 用户名或密码错误); } }// service/StudentService.java public boolean validateLogin(String username, String password) { // ↓ 调用DAO层执行SQL查询 return studentDao.checkCredentials(username, password) 0; }// dao/StudentDao.java public int checkCredentials(String username, String password) { String sql SELECT COUNT(*) FROM student WHERE stu_id ? AND stu_password ?; // ↓ BaseDao封装了Connection获取、PreparedStatement设置、结果集处理 return (int) BaseDao.executeQuery(sql, username, password); }关键点BaseDao.executeQuery()内部做了三件事① 从db.properties读取配置创建Connection② 用PreparedStatement防SQL注入?占位符③ 自动关闭ResultSet/Statement/Connection。这就是为什么它比裸写JDBC更健壮——所有资源释放都在finally块里不会因异常导致连接泄漏。3. 数据库初始化与表结构解析studb.sql里藏着的四个设计决策3.1 建库建表脚本逐行解读为什么selection表用复合主键而不是自增ID打开studb.sql你会发现它不是简单CREATE TABLE堆砌而是包含建库、建表、插入初始数据三段式结构。我们聚焦四张核心表的设计意图-- 1. 创建数据库字符集显式声明避免中文乱码 CREATE DATABASE IF NOT EXISTS studb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; -- 2. 学生表stu_id为主键长度10位兼容学号规则密码明文存储教学场景可接受 CREATE TABLE student ( stu_id VARCHAR(10) PRIMARY KEY, stu_name VARCHAR(20) NOT NULL, stu_password VARCHAR(20) NOT NULL, stu_major VARCHAR(30), stu_grade INT ); -- 3. 教师表tch_id为主键职称字段预留扩展 CREATE TABLE teacher ( tch_id VARCHAR(10) PRIMARY KEY, tch_name VARCHAR(20) NOT NULL, tch_title VARCHAR(20) ); -- 4. 课程表course_id主键credit字段存学分status控制开课状态 CREATE TABLE course ( course_id VARCHAR(10) PRIMARY KEY, course_name VARCHAR(50) NOT NULL, credit INT DEFAULT 2, tch_id VARCHAR(10), status TINYINT DEFAULT 1, -- 1可选0已关闭 FOREIGN KEY (tch_id) REFERENCES teacher(tch_id) ); -- 5. 选课关系表复合主键(stu_id, course_id)天然去重且支持快速查某学生所有课/某课所有学生 CREATE TABLE selection ( stu_id VARCHAR(10), course_id VARCHAR(10), sel_time DATETIME DEFAULT NOW(), PRIMARY KEY (stu_id, course_id), FOREIGN KEY (stu_id) REFERENCES student(stu_id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES course(course_id) ON DELETE CASCADE );设计决策分析selection表不用自增ID因为选课本质是多对多关系stu_id course_id组合天然唯一加ID反而冗余且ON DELETE CASCADE能保证学生删号时自动清理其选课记录course.status字段不是布尔值而是TINYINT为未来扩展留余地如2待审核3已归档student.stu_password明文教学系统不涉及真实密码安全重点展示业务流程若需升级只需在StudentService.login()中加入BCryptPasswordEncoder.encode()即可所有VARCHAR长度精确到业务需求学号10位、姓名20字避免过度分配浪费空间。3.2 初始化数据与业务含义INSERT INTO student插入的6条记录怎么用studb.sql末尾有6条INSERT INTO student语句它们不是随便填的测试数据而是构成最小可行业务闭环INSERT INTO student VALUES (2021001, 张三, 123456, 计算机科学与技术, 2021); INSERT INTO student VALUES (2021002, 李四, 123456, 软件工程, 2021); INSERT INTO student VALUES (2021003, 王五, 123456, 网络工程, 2021); INSERT INTO student VALUES (2022001, 赵六, 123456, 人工智能, 2022); INSERT INTO student VALUES (2022002, 钱七, 123456, 数据科学, 2022); INSERT INTO student VALUES (admin, 管理员, admin, , 0);业务用途前5条是普通学生账号学号前缀2021/2022对应年级方便在StudentPanel.java中实现「按年级筛选学生」功能admin账号是系统管理员密码也是admin登录后进入MainFrame会显示「管理」菜单栏含课程管理、学生管理、教师管理所有学生密码统一为123456降低测试门槛你无需记住6个密码只记一个即可student.stu_grade字段虽未在界面直接展示但在CourseService.getAvailableCoursesByGrade()方法中被用于「高年级学生可选高阶课程」的业务规则。3.3 外键约束与级联删除为什么删教师时课程不会变孤儿course.tch_id是外键指向teacher.tch_id但脚本里没写ON DELETE CASCADE。这意味着删除教师记录时其开设的课程不会被自动删除而是抛出Cannot delete or update a parent row错误。这是有意为之的设计保守策略——防止误操作导致课程数据丢失。验证方法在MySQL命令行执行DELETE FROM teacher WHERE tch_id T001; -- 报错Cannot delete or update a parent row: a foreign key constraint fails (studb.course, CONSTRAINT course_ibfk_1 FOREIGN KEY (tch_id) REFERENCES teacher (tch_id))解决方案教学场景推荐若需支持教师离职时自动下架其课程修改course表外键ALTER TABLE course DROP FOREIGN KEY course_ibfk_1; ALTER TABLE course ADD CONSTRAINT course_ibfk_1 FOREIGN KEY (tch_id) REFERENCES teacher(tch_id) ON DELETE SET NULL;并将tch_id字段改为允许NULLALTER TABLE course MODIFY tch_id VARCHAR(10) NULL;这样删教师时相关课程的tch_id变为NULL状态变为「无教师开设」管理员可后续处理。4. 避坑指南我在Windows 11 MySQL 8.0.33上踩过的5个真实坑4.1 现象运行Main.java报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver原因MySQL 8.0 驱动类名从com.mysql.jdbc.Driver改为com.mysql.cj.jdbc.Driver而源码中BaseDao.java第32行仍写的是旧类名Class.forName(com.mysql.jdbc.Driver); // ← 这行要改解决打开CourseSelection-main/src/dao/BaseDao.java将第32行改为Class.forName(com.mysql.cj.jdbc.Driver);同时db.properties中的url必须加上serverTimezoneGMT%2B8allowPublicKeyRetrievaltrueuseSSLfalse参数否则会报The server time zone value ... is unrecognized或Public Key Retrieval is not allowed。4.2 现象登录成功后主界面空白控制台打印NullPointerException原因MainFrame.java构造函数中调用了initComponents()但该方法内部尝试从student对象取stu_name而student为null。根源在于LoginController.login()中studentService.getStudentByUsername(username)返回null。排查检查studb数据库中是否存在该学号。常见错误是studb.sql执行后忘记USE studb;就直接INSERT导致数据插到mysql系统库。解决在MySQL命令行执行USE studb; SELECT * FROM student WHERE stu_id 2021001; -- 确认有数据若无结果重新执行studb.sql确保开头有USE studb;或在执行时指定数据库mysql -u root -p studb studb.sql。4.3 现象选课按钮点击无反应控制台无报错原因SelectionController.java第45行courseService.getAvailableCoursesByStudentId(student.getStuId())返回空集合但代码未做空判断直接遍历导致for (Course c : courses)循环不执行。解决在SelectionController.java的showSelectionPanel()方法中在for循环前加判空if (courses null || courses.isEmpty()) { JOptionPane.showMessageDialog(frame, 暂无可选课程请联系管理员); return; }4.4 现象Windows下编译报编码GBK的不可映射字符原因源码文件是UTF-8编码但Windows默认cmd是GBK编码javac读取时乱码。解决两种方案任选其一① 在cmd中执行chcp 65001切换为UTF-8编码再运行javac② 用PowerShell替代cmdPowerShell默认UTF-8③ 最彻底用Notepad打开所有.java文件右下角确认编码为UTF-8然后「编码 → 转为UTF-8无BOM格式」保存。4.5 现象MySQL服务启动失败提示Plugin caching_sha2_password could not be loaded原因MySQL 8.0 默认认证插件是caching_sha2_password但mysql-connector-java-5.1.47.jar只支持老版mysql_native_password。解决登录MySQL为root用户切换认证方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 123456; FLUSH PRIVILEGES;注意此操作仅限本地开发环境。生产环境应升级驱动至mysql-connector-java-8.0.33.jar并保持caching_sha2_password。5. 功能增强实战给选课系统加上「退课」按钮与学分实时统计5.1 在StudentPanel.java中添加退课按钮与事件绑定原系统只有「选课」功能但真实场景必须支持退课。我们手动在学生主界面StudentPanel.java底部添加退课按钮// CourseSelection-main/src/view/StudentPanel.java 第120行附近在选课按钮下方 private JButton dropButton; // 声明退课按钮 // 在 initComponent() 方法末尾约第180行添加 dropButton new JButton(退课); dropButton.setFont(new Font(微软雅黑, Font.PLAIN, 14)); dropButton.addActionListener(e - handleDropCourse()); panel.add(dropButton); // 新增退课处理方法放在 initComponent() 外 private void handleDropCourse() { int selectedRow table.getSelectedRow(); if (selectedRow -1) { JOptionPane.showMessageDialog(this, 请先选择一门已选课程); return; } String courseId (String) table.getValueAt(selectedRow, 0); // 获取选中行课程ID int confirm JOptionPane.showConfirmDialog(this, 确定要退选【 table.getValueAt(selectedRow, 1) 】吗, 确认退课, JOptionPane.YES_NO_OPTION); if (confirm JOptionPane.YES_OPTION) { try { // 调用服务层执行退课 boolean success selectionService.dropCourse(student.getStuId(), courseId); if (success) { JOptionPane.showMessageDialog(this, 退课成功); refreshSelectionTable(); // 刷新已选课程列表 refreshAvailableTable(); // 刷新可选课程列表 } else { JOptionPane.showMessageDialog(this, 退课失败请重试); } } catch (Exception ex) { ex.printStackTrace(); JOptionPane.showMessageDialog(this, 系统错误 ex.getMessage()); } } }逻辑说明handleDropCourse()先校验是否选中行再弹窗确认最后调用selectionService.dropCourse()。refreshSelectionTable()和refreshAvailableTable()是已存在的刷新方法确保界面与数据库状态同步。5.2 实现SelectionService.dropCourse()一行SQL完成退课与学分回滚退课不仅是删selection表记录还需更新学生总学分。我们扩展现有service/SelectionService.java// CourseSelection-main/src/service/SelectionService.java public boolean dropCourse(String stuId, String courseId) { String sql DELETE FROM selection WHERE stu_id ? AND course_id ?; int rowsAffected BaseDao.executeUpdate(sql, stuId, courseId); if (rowsAffected 0) { // 退课成功需回滚学分查该课程学分更新学生总学分 String creditSql SELECT credit FROM course WHERE course_id ?; Object result BaseDao.executeQuery(creditSql, courseId); if (result ! null result instanceof Number) { int credit ((Number) result).intValue(); String updateCreditSql UPDATE student SET stu_credit stu_credit - ? WHERE stu_id ?; BaseDao.executeUpdate(updateCreditSql, credit, stuId); } return true; } return false; }参数说明BaseDao.executeUpdate()执行无返回值的SQLINSERT/UPDATE/DELETEBaseDao.executeQuery()执行有返回值的SQLSELECT。这里用两个独立SQL保证原子性——虽然不如事务严谨但教学系统够用。若需强一致性可在BaseDao中封装beginTransaction()/commit()方法。5.3 在学生信息栏动态显示「已修学分」从student表实时计算原界面只显示学生基本信息我们让它在右上角实时显示「已修学分」。修改StudentPanel.java的initComponent()方法在添加完所有组件后插入// 在 panel.add(scrollPane) 之后添加 JLabel creditLabel new JLabel(已修学分0); creditLabel.setFont(new Font(微软雅黑, Font.BOLD, 16)); creditLabel.setForeground(Color.BLUE); panel.add(creditLabel); // 新增刷新学分方法在类末尾 private void refreshCreditDisplay() { String sql SELECT COALESCE(SUM(c.credit), 0) FROM selection s JOIN course c ON s.course_id c.course_id WHERE s.stu_id ?; Object result BaseDao.executeQuery(sql, student.getStuId()); int totalCredit result instanceof Number ? ((Number) result).intValue() : 0; creditLabel.setText(已修学分 totalCredit); }关键点COALESCE(SUM(c.credit), 0)解决学生未选任何课时SUM返回NULL的问题强制转为0refreshCreditDisplay()应在每次选课/退课后调用确保实时性。5.4 验证增强功能用三条SQL语句确认退课逻辑正确性功能加完不是终点必须用数据库层面验证。打开MySQL命令行执行以下三步Step 1查学生初始学分SELECT stu_id, stu_name, stu_credit FROM student WHERE stu_id 2021001; -- 输出2021001, 张三, 0 初始为0Step 2模拟选课原系统已有INSERT INTO selection (stu_id, course_id) VALUES (2021001, C001); -- C001课程学分为3执行后 stu_credit 应变为3Step 3执行退课并验证学分回滚DELETE FROM selection WHERE stu_id 2021001 AND course_id C001; SELECT COALESCE(SUM(c.credit), 0) FROM selection s JOIN course c ON s.course_id c.course_id WHERE s.stu_id 2021001; -- 输出0 证明学分已清零血泪经验我第一次加退课功能时忘了在dropCourse()里更新stu_credit导致学生退课后学分没减少下次选课时因「学分超限」被拒绝。后来养成习惯任何影响学生状态的操作选/退课、修改年级必须在数据库层面用SELECT验证最终值。从那以后我每次改完DAO方法都强制走一遍这三步SQL验证再启动Java程序看界面是否同步。希望帮到你。本文还有配套的精品资源点击获取
返回列表