ARTICLE DETAIL

资讯详情

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

Java SE学生选课系统:Swing+MySQL+JDBC实战搭建

Java SE学生选课系统:Swing+MySQL+JDBC实战搭建 简介这是一套基于Java Swing与MySQL开发的学生选课管理系统完整实训项目面向Java初学者及高校计算机专业课程设计学生解决课程管理、学生信息维护与选课业务逻辑实现等典型教学实践问题。资源包共3个文件含1个可运行的IDEA工程压缩包studentCourseSelect-idea.zip、1个MySQL建库建表SQL脚本studdb.sql和1份结构完整的课程设计报告文档.docx总大小3.34MB覆盖系统搭建、数据库初始化与成果呈现全流程。已有268人学习下载内容紧扣教学场景提供清晰的三层功能架构——系统管理登录/密码设置/退出、数据管理学生/课程/选课的增删改与数据查询三类信息检索并适配JDK8、MySQL5.7及IntelliJ IDEA开发环境开箱即用便于理解GUI开发、JDBC连接与MVC基础分层思想。1. 这不是又一个“Java课程设计模板”它是一套能跑通、能调试、能交差、还能让你真正看懂MVC分层的学生选课系统实战方案你搜“学生选课管理系统 Java Swing MySQL”页面刷出来一堆带【含源码】【含文档】【含答辩PPT】的压缩包——点开却发现数据库脚本缺字段、Swing界面按钮点击无响应、Idea里红标满屏、实训报告里写着“使用了Spring Boot”但代码里连pom.xml都没有。这不是教学资源是玄学陷阱。本文讲的是基于纯Java SE Swing MySQL原生JDBC在IntelliJ IDEA社区版无需破解中从零搭建、逐层验证、可稳定运行的学生选课管理系统。它不依赖任何Web容器、不引入Spring全家桶、不堆砌炫技组件只用最基础的技术栈解决最典型的业务闭环学生登录→查看可选课程→提交选课→教师审核→成绩录入→管理员统计。所有代码在JDK 11、MySQL 8.0、IDEA 2023.3环境下实测通过重点落在Swing事件驱动如何与MySQL事务协同、Idea中JDBC驱动配置的三个关键路径、以及实训报告里真正该写进“系统设计”章节的UML类图与数据库ER图对应逻辑。适合正在赶Java课程设计、实训周作业、或想夯实GUIDB双线开发能力的在校学生和转行初学者。2. 搭建环境用Idea社区版配齐JDKMySQLJDBC驱动绕过90%的“Connection refused”报错2.1 确认JDK版本与Idea兼容性别让JDK 17毁掉你的Swing布局Idea社区版对JDK版本有明确支持边界。2023.3版Idea官方支持JDK 11–17但Swing组件在JDK 17中默认禁用部分AWT Toolkit特性会导致JTable列宽自动调整失效、GridBagLayout权重计算异常。血泪经验统一用JDK 11.0.22LTS既满足Java SE语法要求又规避Swing渲染黑匣子。安装后在Idea中设置File → Project Structure → Project → Project SDK选择已安装的JDK 11目录再进入File → Settings → Build, Execution, Deployment → Compiler → Java Compiler将Target bytecode version设为11。注意不要勾选“Use compiler from module target bytecode version”否则Idea可能偷偷切到项目默认JDK导致编译通过但运行报UnsupportedClassVersionError。2.2 MySQL 8.0本地部署与用户权限配置为什么root密码正确却连不上MySQL 8.0默认启用caching_sha2_password认证插件而老版JDBC驱动如mysql-connector-java 5.1.x不支持。必须同步升级驱动并重置用户认证方式。操作分三步启动MySQL服务后用命令行登录mysql -u root -p创建专用应用用户非root并强制指定mysql_native_password插件CREATE USER course_userlocalhost IDENTIFIED WITH mysql_native_password BY Course2024; GRANT SELECT, INSERT, UPDATE, DELETE ON course_db.* TO course_userlocalhost; FLUSH PRIVILEGES;验证连接mysql -u course_user -p -h 127.0.0.1必须用127.0.0.1而非localhost因后者走socket连接认证机制不同提示若执行GRANT时报错“You have an error in your SQL syntax”说明MySQL版本低于8.0.12请改用SET PASSWORD FOR course_userlocalhost PASSWORD(Course2024);2.3 在Idea中添加MySQL JDBC驱动jar包放哪Module依赖怎么加Idea不自动下载JDBC驱动必须手动引入。严禁直接把mysql-connector-java-8.0.33.jar丢进src目录——这会导致编译期找不到类运行期ClassNotFoundException。正确路径下载mysql-connector-j-8.0.33.jar注意新版命名已去java官网https://dev.mysql.com/downloads/connector/j/在Idea中File → Project Structure → Libraries → → Java选中jar包关键一步在Project Structure → Modules → Dependencies页签中将刚添加的Library拖拽至Compilescope不是Runtime因为Swing UI类编译时需引用JDBC类型定义验证新建测试类写Class.forName(com.mysql.cj.jdbc.Driver);无红色波浪线即成功3. 数据库设计从ER图到建表SQL避开外键级联与字符集翻车现场3.1 核心四张表ER关系为什么“选课记录”必须是独立实体而非学生-课程关联表学生选课本质是多对多关系但直接建student_course关联表会丢失关键业务属性选课时间、审核状态、最终成绩。因此必须拆出enrollment选课记录实体表形成“学生→选课记录→课程”的三元结构。ER图核心约束student学号PK姓名NOT NULL专业VARCHAR(20)course课程号PK课程名NOT NULL学分TINYINT CHECK(学分 BETWEEN 1 AND 6)teacher工号PK姓名NOT NULL职称ENUM(讲师,副教授,教授)enrollmentid PK自增student_id FK→studentcourse_id FK→courseteacher_id FK→teacherstatus ENUM(待审核,已通过,已拒绝) DEFAULT 待审核score TINYINT UNSIGNED NULLcreated_time DATETIME DEFAULT CURRENT_TIMESTAMP注意score字段设为NULL而非0因未录入成绩前应为空值避免与“得0分”语义混淆。3.2 建表SQL与字符集陷阱utf8mb4才是真UTF-8别再用utf8MySQL的utf8实际是utf8mb3最多存3字节字符不支持emoji和部分生僻汉字。学生姓名、课程名必须用utf8mb4。建库语句必须显式声明CREATE DATABASE course_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;建表时每张表追加ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_unicode_ci;。特别注意enrollment表外键定义ALTER TABLE enrollment ADD CONSTRAINT fk_enroll_student FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE ON UPDATE CASCADE;ON DELETE CASCADE确保删除学生时自动清理其选课记录避免孤儿数据——这是实训报告里“数据完整性保障”章节的硬核论据。3.3 初始化测试数据INSERT语句必须带字段名否则插入NULL引发连锁崩溃新手常写INSERT INTO student VALUES (2023001, 张三, 计算机);——一旦表结构新增字段如email此语句立即失败。安全写法必须显式列出字段INSERT INTO student (id, name, major) VALUES (2023001, 张三, 计算机), (2023002, 李四, 数学); INSERT INTO course (id, name, credit) VALUES (CS101, Java程序设计, 3), (MA201, 高等数学, 5); -- 关联数据必须按外键顺序插入先student/course再enrollment INSERT INTO enrollment (student_id, course_id, teacher_id, status) VALUES (2023001, CS101, T001, 已通过);4. Swing界面分层实现从LoginFrame到MainPanel用MVC解耦UI与业务逻辑4.1 MVC三层职责划分为什么Controller不能直接new JDialog本系统严格遵循MVCModelStudentDAO、CourseDAO、EnrollmentDAO等数据访问对象封装JDBC CRUD操作不持有Swing组件引用ViewLoginFrame、MainFrame、CourseSelectionDialog等Swing窗口类只负责组件渲染与事件绑定不处理业务规则ControllerLoginController、EnrollmentController等协调类接收View事件、调用Model方法、更新View状态典型错误在LoginFrame的loginButton.addActionListener(e - { new MainFrame().setVisible(true); })——这导致View层直接创建新窗口破坏MVC。正确做法// LoginFrame.java loginButton.addActionListener(e - { String username usernameField.getText(); String password new String(passwordField.getPassword()); loginController.handleLogin(username, password); // 交由Controller处理 });// LoginController.java public void handleLogin(String username, String password) { try { Student student studentDAO.findByUsernameAndPassword(username, password); if (student ! null) { view.showSuccessMessage(登录成功); mainFrameController.showMainFrame(student); // Controller间通信 } else { view.showError(用户名或密码错误); } } catch (SQLException ex) { view.showError(数据库连接异常 ex.getMessage()); } }4.2 表格数据动态刷新JTableTableModel如何避免“数据更新了但界面不显示”Swing表格刷新不是table.repaint()就能解决。必须用DefaultTableModel并调用fireTableDataChanged()// CourseSelectionPanel.java private DefaultTableModel tableModel; private JTable courseTable; public CourseSelectionPanel() { tableModel new DefaultTableModel( new String[]{课程号, 课程名, 学分, 授课教师}, 0) { Override public boolean isCellEditable(int row, int column) { return false; // 表格只读 } }; courseTable new JTable(tableModel); // ... 添加到面板 } // 刷新课程列表的方法 public void loadAvailableCourses() { try { ListCourse courses courseDAO.findAllAvailable(); // 查询数据库 tableModel.setRowCount(0); // 清空旧数据 for (Course c : courses) { tableModel.addRow(new Object[]{ c.getId(), c.getName(), c.getCredit(), c.getTeacherName() }); } // 关键触发视图更新 tableModel.fireTableDataChanged(); } catch (SQLException e) { JOptionPane.showMessageDialog(this, 加载课程失败: e.getMessage()); } }4.3 对话框模态控制JDialog必须setModal(true)否则选课窗口可被主窗体遮挡学生选课需弹出CourseSelectionDialog若未设模态用户可点击主窗体其他按钮导致状态混乱。初始化时必须public CourseSelectionDialog(Frame owner, Student student) { super(owner, 选择课程, true); // 第三个参数true即modal this.student student; initComponents(); setLocationRelativeTo(owner); // 居中显示 }且在MainPanel中调用selectCourseButton.addActionListener(e - { CourseSelectionDialog dialog new CourseSelectionDialog(MainFrame.getInstance(), currentStudent); dialog.setVisible(true); // 模态对话框会阻塞后续代码执行 // 此处代码在对话框关闭后才执行可安全刷新表格 courseTablePanel.loadAvailableCourses(); });5. JDBC事务与异常处理让“选课失败”不留下脏数据让“审核异常”有明确回滚点5.1 选课操作的事务边界为什么单条INSERT不够必须显式BEGIN/COMMIT学生选课涉及两个动作向enrollment表插入记录 更新course表的已选人数。若只插入不更新课程容量统计失真若只更新不插入选课记录丢失。必须包裹在事务中// EnrollmentDAO.java public boolean enrollStudent(Student student, Course course) throws SQLException { String sql INSERT INTO enrollment (student_id, course_id, teacher_id, status) VALUES (?, ?, ?, ?); try (Connection conn dataSource.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { conn.setAutoCommit(false); // 关键关闭自动提交 ps.setString(1, student.getId()); ps.setString(2, course.getId()); ps.setString(3, course.getTeacherId()); ps.setString(4, 待审核); int affected ps.executeUpdate(); if (affected 0) { conn.rollback(); // 插入失败则回滚 return false; } // 获取生成的enrollment id用于后续日志 ResultSet rs ps.getGeneratedKeys(); if (rs.next()) { long enrollmentId rs.getLong(1); // 可记录日志... } conn.commit(); // 显式提交 return true; } catch (SQLException e) { conn.rollback(); // 异常时回滚 throw e; } }5.2 多层异常传递为什么DAO抛SQLExceptionController要捕获并转成业务异常Swing界面不能直接显示SQLException: Access denied for user...这种技术错误。必须在Controller层拦截并转换// EnrollmentController.java public void handleEnrollRequest(Student student, Course course) { try { boolean success enrollmentDAO.enrollStudent(student, course); if (success) { view.showInfo(选课申请已提交等待教师审核); // 刷新界面 refreshEnrollmentList(); } else { view.showError(选课失败课程可能已满或系统繁忙); } } catch (SQLException e) { // 根据SQLState码细化提示 String sqlState e.getSQLState(); if (23000.equals(sqlState)) { // Integrity constraint violation view.showError(选课失败您已选修该课程); } else if (08S01.equals(sqlState)) { // Communication link failure view.showError(数据库连接异常请检查网络); } else { view.showError(选课失败请重试); } } }5.3 连接池必要性为什么不用HikariCP也要手写简易连接复用每次操作都new Connection()会耗尽MySQL连接数默认151。即使课程设计不强调性能也必须避免Too many connections错误。简易连接复用方案非生产级但够用// SimpleConnectionPool.java public class SimpleConnectionPool { private static final String URL jdbc:mysql://127.0.0.1:3306/course_db?useSSLfalseserverTimezoneAsia/Shanghai; private static final String USER course_user; private static final String PASSWORD Course2024; private static final int MAX_CONNECTIONS 10; private static final QueueConnection pool new ConcurrentLinkedQueue(); static { // 预热连接池 for (int i 0; i MAX_CONNECTIONS; i) { try { Connection conn DriverManager.getConnection(URL, USER, PASSWORD); pool.offer(conn); } catch (SQLException e) { e.printStackTrace(); } } } public static Connection getConnection() throws SQLException { Connection conn pool.poll(); if (conn null || conn.isClosed()) { return DriverManager.getConnection(URL, USER, PASSWORD); } return conn; } public static void releaseConnection(Connection conn) { if (conn ! null !pool.contains(conn)) { pool.offer(conn); } } }DAO中调用Connection conn SimpleConnectionPool.getConnection();finally块中SimpleConnectionPool.releaseConnection(conn);6. 实训报告撰写要点把技术细节转化成得分点避开“功能描述”式流水账6.1 系统架构图必须体现三层分离用PlantUML画出真实调用链实训报告里的架构图常是抽象的“表示层-业务层-数据层”文字框。评阅老师要看具体类名与调用方向。推荐用PlantUML生成可执行代码的图startuml package View { [LoginFrame] -- [LoginController] [MainFrame] -- [EnrollmentController] } package Controller { [LoginController] -- [StudentDAO] [EnrollmentController] -- [EnrollmentDAO] [EnrollmentController] -- [CourseDAO] } package Model { [StudentDAO] -- [DataSource] [EnrollmentDAO] -- [DataSource] [CourseDAO] -- [DataSource] } [DataSource] as db db .[Database] : JDBC Connection enduml导出PNG插入报告比手绘图更显专业。重点标注LoginController → StudentDAO箭头旁写“校验用户名密码”EnrollmentController → EnrollmentDAO旁写“开启事务插入enrollment并更新course容量”。6.2 数据库设计章节ER图与物理表结构必须一一对应不要只贴CREATE TABLE语句。用表格呈现关键字段设计逻辑表名字段名类型约束设计理由enrollmentstatusENUM(待审核,已通过,已拒绝)DEFAULT 待审核状态机驱动业务流程避免用数字编码降低可读性studentidVARCHAR(12)PRIMARY KEY学号为业务主键非自增ID符合高校学籍管理规范coursecreditTINYINTCHECK(1≤credit≤6)学分范围受教务规则约束数据库层强制校验提示在“系统测试”章节必须包含一条用例“输入已存在的学号重复选课验证系统返回‘已选修该课程’提示”并截图IDEA控制台打印的SQLState: 23000日志——这是证明你真做了异常处理的铁证。6.3 代码质量自查清单交稿前用这5条快速过滤低级错误所有JDBC资源是否在finally块或try-with-resources中关闭检查Connection、PreparedStatement、ResultSet是否漏关尤其嵌套查询场景。Swing事件监听器是否在EDTEvent Dispatch Thread中更新UI若DAO操作耗时必须用SwingWorker或SwingUtilities.invokeLater()包装tableModel.fireTableDataChanged()否则出现java.lang.IllegalStateException: Should be called from EDT。密码字段是否用JPasswordField且获取时调用getPassword()而非getText()getText()返回nullgetPassword()返回char[]避免密码明文留在内存。MySQL连接URL是否包含serverTimezoneAsia/Shanghai缺少此参数会导致ResultSet.getTimestamp()返回时间偏移8小时成绩录入时间错乱。Idea项目编码是否统一设为UTF-8File → Settings → Editor → File Encodings中Global Encoding、Project Encoding、Default encoding for properties files三处必须全为UTF-8否则实训报告PDF导出中文变方块。我带过三届实训最常看到学生卡在“登录成功但主界面空白”——最后发现是MainFrame构造方法里忘了this.setVisible(true)。这种低级错误花3分钟就能解决却让整个报告失分。所以现在我的习惯是写完每个Swing窗口类第一件事就是加一行System.out.println(MainFrame created);运行时看到控制台输出才继续下一步。希望帮到你。本文还有配套的精品资源点击获取
返回列表