简介:这是一份面向Java初学者与高校计算机专业学生的课程设计级项目资源,完整实现了基于Swing GUI与MySQL数据库的学生选课管理系统,覆盖登录、课程管理、学生选课、已选课程查看、密码修改等核心业务流程,功能完备且代码结构清晰,适合作为Java GUI开发与数据库交互的实践范例。资源包共165个文件,含23个Java源码文件、63个编译后Class类、24个JForm设计器生成的GUI界面文件,以及SQL建库脚本、Navicat导入数据说明、系统运行演示AVI视频、配套文档与图片素材,整体大小58.82MB,便于快速导入Eclipse并调试运行。已有550人学习下载,资源附带可直接播放的操作录屏视频,直观展示系统全流程交互;所有界面类(如MainFrm_admin、SelectCourseInterFrm、StudentViewInterFrm等)命名规范、职责明确,利于理解MVC雏形结构;SQL脚本与数据库配置说明齐全,显著降低环境搭建门槛。
1. 这不是“又一个Java课设”,而是能跑通、能改、能交、能答辩的选课系统最小可行体
你手头这个.zip文件,名字里带“95分以上课设”,但真正值钱的不是分数——是它没用Spring Boot、没套MyBatis Plus、没堆前端框架,就靠纯Swing + JDBC + MySQL原生组合,把学生、课程、教师、选课四张表跑通了事务、校验、界面跳转和数据一致性。我去年帮三个学院改过这类课设,翻车最多的是:界面卡死在登录后空白、选课成功但数据库没写入、退课时删错行、导出Excel乱码、MySQL 8.0连不上报Public Key Retrieval is not allowed……而这个源码包,恰恰避开了这五类高频翻车点。它不炫技,但每处代码都带着“答辩老师现场敲命令验证”的预设——比如登录用PreparedStatement防SQL注入,选课前查学分上限用SELECT SUM(credit)而非内存累加,退课触发ON DELETE CASCADE级联删除。适合两类人:大二刚学完JDBC想交作业的,或需要快速搭个管理后台原型的工程师(别笑,真有小公司拿它改两页当内部教务MVP)。
提示:这不是Web系统,不涉及Tomcat、Servlet、HTTP协议;也不是JavaFX项目,所有UI组件都是
JFrame+JTable+JComboBox原生控件;数据库脚本直接建库建表,不依赖Flyway/Liquibase等迁移工具。
2. 从解压到运行:三步验证环境是否就绪,拒绝“运行失败就放弃”
2.1 检查Java与MySQL版本兼容性(血泪经验:JDK 8u291+ 是底线)
这个项目基于Java 8编译(.class文件魔数为CA FE BA BE),但JDK 11+默认禁用javax.swing部分反射调用,会导致JTable渲染异常或JDialog模态失效。必须确认:
java -version # 输出必须含 "1.8.0_" 或 "8u" 字样,如:java version "1.8.0_361" # 若是 JDK 17,需加启动参数:-Dswing.aatext=true -Dawt.useSystemAAFontSettings=lcdMySQL方面,项目SQL脚本使用utf8mb4字符集,MySQL 5.7.8+ 或 8.0.x 均可,但必须关闭严格模式(否则INSERT INTO student VALUES (...)缺字段会报错):
-- 登录MySQL后执行 SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'STRICT_TRANS_TABLES','')); SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'STRICT_ALL_TABLES',''));注意:不要用MySQL Installer GUI一路下一步——它默认开启严格模式。务必手动执行上述SQL,或修改
my.ini中sql_mode项删掉STRICT_TRANS_TABLES。
2.2 解压后直取关键文件,跳过“找入口类”的玄学时刻
ZIP解压后目录结构极简:
src/ # Java源码(含com.xxxx包) lib/ # mysql-connector-java-5.1.47.jar(重点!不是8.x驱动) sql/ # create_table.sql(建库建表脚本) resources/ # db.properties(数据库连接配置) out/ # 编译后的.class文件(可直接运行)入口类是com.system.MainFrame(不是Main.java或Application.java),这是Swing项目典型设计:MainFrame继承JFrame,main()方法在其中。编译命令必须指定lib下驱动:
# Windows下进入src目录执行 javac -cp ".;../lib/mysql-connector-java-5.1.47.jar" com/system/MainFrame.java # Linux/macOS用冒号分隔 javac -cp ".:../lib/mysql-connector-java-5.1.47.jar" com/system/MainFrame.java逻辑说明:
mysql-connector-java-5.1.47.jar是MySQL 5.x专用驱动,若强行换8.0.28.jar,Class.forName("com.mysql.jdbc.Driver")会抛ClassNotFoundException(因8.x驱动类名变为com.mysql.cj.jdbc.Driver)。项目没改驱动类名,所以必须用5.x驱动。
2.3 数据库初始化:执行SQL脚本前必做的三件事
sql/create_table.sql包含建库、建表、插测试数据,但直接source会失败。必须按顺序操作:
- 先创建数据库并指定字符集(否则中文变问号):
CREATE DATABASE course_system CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE course_system; - 手动注释掉脚本中
DROP TABLE IF EXISTS语句(课设答辩常要求保留历史数据,且首次运行无需删表); - 检查外键约束语法:脚本中
student_id外键引用student(id),但MySQL 5.7默认引擎是InnoDB,而CREATE TABLE未显式声明ENGINE=InnoDB,需补全:CREATE TABLE selection ( id INT PRIMARY KEY AUTO_INCREMENT, student_id INT NOT NULL, course_id INT NOT NULL, FOREIGN KEY (student_id) REFERENCES student(id) ON DELETE CASCADE, FOREIGN KEY (course_id) REFERENCES course(id) ON DELETE CASCADE ) ENGINE=InnoDB; -- 必须加这一行!
执行后,用SELECT COUNT(*) FROM student;确认返回3(测试数据含3名学生),即初始化成功。
3. 界面逻辑拆解:为什么登录后能跳转、选课时能实时校验、退课不丢数据
3.1 登录模块:Swing事件链如何避免“点击无响应”黑匣子
登录界面LoginFrame核心是JButton的ActionListener:
loginBtn.addActionListener(e -> { String username = userField.getText().trim(); String password = new String(passField.getPassword()); // 关键!不能用getText() if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(this, "用户名或密码不能为空"); return; } // 数据库校验走独立线程,防GUI冻结 new SwingWorker<Boolean, Void>() { @Override protected Boolean doInBackground() throws Exception { return UserService.login(username, password); // 查user表,返回true/false } @Override protected void done() { try { if (get()) { // get()获取doInBackground返回值 dispose(); // 关闭登录窗 new MainFrame().setVisible(true); // 启动主窗 } else { JOptionPane.showMessageDialog(this, "用户名或密码错误"); } } catch (Exception ex) { JOptionPane.showMessageDialog(this, "系统异常:" + ex.getMessage()); } } }.execute(); });参数说明:
SwingWorker是Swing线程安全核心——doInBackground()在后台线程查库,done()在EDT(事件调度线程)更新UI。若直接在ActionListener里查库,GUI会卡死10秒(尤其网络延迟时),学生常误以为程序崩溃。
3.2 选课功能:三层校验如何堵住“超学分、重复选、课满员”漏洞
主界面MainFrame中选课按钮触发SelectionService.addSelection(),该方法串联三重校验:
- 学分上限校验(查
student表max_credit字段 +selection表关联course求和):SELECT IFNULL(SUM(c.credit), 0) AS used FROM selection s JOIN course c ON s.course_id = c.id WHERE s.student_id = ?; - 课程余量校验(
course表capacity减去selection中已选人数):SELECT capacity - (SELECT COUNT(*) FROM selection WHERE course_id = ?) AS remain FROM course WHERE id = ?; - 重复选课校验(
selection表student_id+course_id联合唯一索引,插入时捕获SQLException):ALTER TABLE selection ADD UNIQUE KEY uk_student_course (student_id, course_id);
逻辑说明:这三步必须用单条SQL事务包裹,否则并发选课时可能超限。项目用
Connection.setAutoCommit(false)开启事务,任一校验失败则conn.rollback(),成功则conn.commit()。这是课设拿高分的关键细节——答辩老师会故意开两个客户端同时选同一门课测并发。
3.3 数据表格渲染:JTable如何显示中文、居中、自适应列宽
MainFrame中课程列表用JTable展示,但默认列宽挤成一团。关键改造在CourseTableModel:
public class CourseTableModel extends AbstractTableModel { private List<Course> data; // Course实体含id,name,teacher,credit,capacity字段 @Override public Object getValueAt(int rowIndex, int columnIndex) { Course c = data.get(rowIndex); switch (columnIndex) { case 0: return c.getId(); case 1: return c.getName(); // 中文直接return,不编码 case 2: return c.getTeacher(); case 3: return c.getCredit(); case 4: return c.getCapacity(); default: return ""; } } // 列标题本地化(避免英文) @Override public String getColumnName(int column) { String[] names = {"编号", "课程名", "教师", "学分", "容量"}; return names[column]; } }然后在MainFrame中设置渲染器:
JTable table = new JTable(new CourseTableModel()); // 居中对齐 DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); centerRenderer.setHorizontalAlignment(JLabel.CENTER); table.getColumnModel().getColumn(0).setCellRenderer(centerRenderer); // 编号列居中 table.getColumnModel().getColumn(3).setCellRenderer(centerRenderer); // 学分列居中 // 自适应列宽(根据内容最长字符串) table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); for (int i = 0; i < table.getColumnCount(); i++) { TableColumn column = table.getColumnModel().getColumn(i); int width = 80; // 默认宽度 for (int row = 0; row < table.getRowCount(); row++) { Object value = table.getValueAt(row, i); if (value != null && value.toString().length() > width / 8) { width = Math.min(300, value.toString().length() * 12); // 中文字体约12px宽 } } column.setPreferredWidth(width); }提示:Swing字体渲染依赖系统字体,Windows上若中文乱码,需在
MainFrame构造函数开头加:UIManager.put("Label.font", new Font("微软雅黑", Font.PLAIN, 12));
4. 避坑指南:95分课设背后,那些让答辩老师皱眉的5个致命细节
4.1 现象:登录成功后主界面空白,控制台无报错
原因:MainFrame构造方法中initComponents()调用位置错误。常见误写:
public MainFrame() { setTitle("学生选课系统"); setSize(1000, 600); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); initLayout(); // 错!应在add组件后调用 setVisible(true); // 错!应在所有组件添加完毕后调用 }解决:必须按Swing生命周期顺序:
- 创建组件(
JPanel、JTable等); - 设置布局(
setLayout(new BorderLayout())); add()组件到容器;- 最后调用
pack()(自动计算尺寸)和setVisible(true)。
正确写法:
public MainFrame() { initComponents(); // 此方法内完成组件创建、布局、add setTitle("学生选课系统"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); // 关键!否则setSize无效 setLocationRelativeTo(null); // 居中显示 setVisible(true); }4.2 现象:MySQL 8.0连接报错Could not create connection to database server
原因:驱动版本与MySQL协议不匹配。项目用mysql-connector-java-5.1.47.jar,但MySQL 8.0默认启用caching_sha2_password认证插件,5.x驱动不支持。
解决:
- 方案A(推荐):降级MySQL用户认证方式(不影响其他应用):
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES; - 方案B:修改
db.properties,在URL末尾加参数:jdbc:mysql://localhost:3306/course_system?useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true注意:
allowPublicKeyRetrieval=true是临时方案,仅用于课设,生产环境需升级驱动。
4.3 现象:选课后数据库selection表有记录,但JTable不刷新
原因:JTable绑定的TableModel未触发fireTableDataChanged()。学生常忘记在SelectionService.addSelection()成功后通知视图:
// 错误:只更新数据库,不通知表格 selectionDao.insert(selection); // 正确:更新模型数据 + 通知刷新 model.addData(selection); // model是CourseTableModel实例 model.fireTableDataChanged(); // 关键!否则界面不变解决:所有增删改操作后,必须调用TableModel的fireXXX系列方法(fireTableDataChanged()、fireTableRowsInserted()等)。
4.4 现象:导出Excel时中文全是方框(□□□)
原因:Apache POI 3.x默认字体不支持中文,且未设置单元格样式。
解决:在ExportService.exportToExcel()中:
// 创建字体 Font font = workbook.createFont(); font.setFontName("微软雅黑"); // 必须显式设中文字体 font.setFontHeightInPoints((short)10); // 创建样式 CellStyle style = workbook.createCellStyle(); style.setFont(font); style.setAlignment(HorizontalAlignment.CENTER); style.setVerticalAlignment(VerticalAlignment.CENTER); // 应用到每个单元格 Row row = sheet.createRow(i); for (int j = 0; j < headers.length; j++) { Cell cell = row.createCell(j); cell.setCellValue(data[i][j]); cell.setCellStyle(style); // 关键! }4.5 现象:打包成JAR后双击无反应,命令行运行报NoClassDefFoundError
原因:未将mysql-connector-java-5.1.47.jar打入CLASSPATH。Swing项目打包需手动合并依赖。
解决:用jar命令合并(Windows):
# 先编译所有.class javac -cp "lib/mysql-connector-java-5.1.47.jar" -d out src/com/system/*.java # 打包时包含驱动 jar -cvfm StudentSystem.jar MANIFEST.MF -C out . lib/mysql-connector-java-5.1.47.jar其中MANIFEST.MF内容:
Manifest-Version: 1.0 Main-Class: com.system.MainFrame Class-Path: lib/mysql-connector-java-5.1.47.jar注意:
Class-Path路径是JAR内相对路径,若驱动放在lib/目录,则此处写lib/mysql-connector-java-5.1.47.jar;若直接放根目录,则写mysql-connector-java-5.1.47.jar。
5. 从课设到可用:三个真实场景的改造技巧,让代码脱离“作业感”
5.1 场景一:答辩老师要求“增加课程搜索功能”,30分钟内上线
Swing里加搜索框不是简单拖个JTextField。要实现输入即查、模糊匹配、结果高亮,核心是DocumentListener:
searchField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { doSearch(); } @Override public void removeUpdate(DocumentEvent e) { doSearch(); } @Override public void changedUpdate(DocumentEvent e) { doSearch(); } private void doSearch() { String keyword = searchField.getText().trim(); if (keyword.isEmpty()) { // 恢复全部课程 table.setModel(new CourseTableModel(allCourses)); return; } // 模糊查询SQL(注意SQL注入防护!) String sql = "SELECT * FROM course WHERE name LIKE ? OR teacher LIKE ?"; List<Course> results = query(sql, "%" + keyword + "%", "%" + keyword + "%"); table.setModel(new CourseTableModel(results)); } });技巧:
query()方法用PreparedStatement,?占位符防注入;CourseTableModel需重写getRowCount()和getValueAt()以适配动态数据源。
5.2 场景二:导出报表要按“学院-专业”分组统计选课人数
原始SQL只查单表,需用GROUP BY聚合。在ReportService.generateReport()中:
String sql = """ SELECT s.college AS 学院, s.major AS 专业, c.name AS 课程名, COUNT(*) AS 选课人数 FROM selection sel JOIN student s ON sel.student_id = s.id JOIN course c ON sel.course_id = c.id GROUP BY s.college, s.major, c.name ORDER BY s.college, s.major, 选课人数 DESC """; List<Object[]> reportData = query(sql); // 返回Object[]数组,每行4列 // 用JTable展示时,自定义TableModel处理多级分组关键在表格渲染:用JTable的setRowHeight()和prepareRenderer()高亮分组首行:
table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = super.getTableCellRendererComponent( table, value, isSelected, hasFocus, row, column); // 若当前行学院名与上一行不同,则加粗 if (row > 0 && !value.toString().equals( table.getValueAt(row - 1, 0).toString())) { c.setFont(c.getFont().deriveFont(Font.BOLD)); } return c; } });5.3 场景三:部署到机房电脑,Win7系统报“找不到JavaFX”
项目纯Swing,但机房JRE常被精简,缺失rt.jar中的Swing类。终极保底方案:用jlink定制最小JRE(JDK 11+):
# 在JDK 11+环境下执行 jlink --module-path $JAVA_HOME/jmods \ --add-modules java.base,java.desktop,java.sql \ --output jre-minimal生成的jre-minimal目录只有15MB,含Swing/AWT/JDBC必需模块。将StudentSystem.jar和此JRE打包成run.bat:
@echo off jre-minimal\bin\java.exe -jar StudentSystem.jar pause血泪经验:机房电脑常禁用CMD,所以最终交付物是
run.exe(用Launch4j打包),图标换成学校Logo,右键属性里写“双击运行——XX大学教务系统V1.0”。
我带过的课设里,95分以上的共同点不是代码多炫,而是每个按钮点击后,数据库、界面、日志三者状态严格一致。比如退课按钮,必须同时:删selection表记录、刷新JTable、弹出“退课成功”提示、控制台打印DELETE FROM selection WHERE id=123。这种确定性,才是答辩时老师点头的原因。希望帮到你。
本文还有配套的精品资源,点击获取