Java会议预约桌面程序源码:Swing界面+MySQL8.0直连+建库脚本+9张截图+完整Maven结构

Java会议预约桌面程序源码:Swing界面+MySQL8.0直连+建库脚本+9张截图+完整Maven结构
本文还有配套的精品资源点击获取简介一套开箱即用的Java会议预约管理系统纯Swing开发的桌面端应用JDBC方式连接MySQL 8.0数据库无需额外框架。包含完整的建库建表SQL脚本meeting.sql已预置用户、会议室、会议安排三张核心表及初始化测试数据支持会议创建、预约登记、按条件查询、删除等全流程操作。项目采用标准Maven结构pom.xml已配置好JDK 12兼容依赖src/main/java和src/main/resources目录清晰分离可直接导入IntelliJ IDEA或Eclipse运行。配套提供9张真实界面截图png格式覆盖登录、主界面、预约表单、查询结果等关键页面readme.md含环境配置说明、运行步骤和功能说明所有Java源文件编码为GBK适配国内常见教学与开发环境。图片资源统一放在img目录下SQL脚本存于根目录和sql子目录双备份mvn-settings.xml便于本地Maven仓库快速适配。1. 这不是Demo是能直接进课堂的“教学级生产环境”我带过六届数据库与Java课程设计每年最头疼的不是学生写不出代码而是他们花三周时间卡在环境配不起来、SQL连不上、Swing界面一启动就报UnsupportedCharsetException——最后交上来一个连登录框都弹不出来的空壳项目。直到去年我把这套会议预约系统定为标准参考模板情况才真正扭转。它不是那种“跑通了但全是硬编码”的玩具工程也不是套着Spring Boot外壳、实则把所有逻辑塞进Controller里的教学陷阱。它用最朴素的JDBC直连MySQL 8.0所有SQL语句手写、参数预编译、事务手动控制Swing界面不用任何第三方UI库纯AWTSwing组件堆叠但布局管理器选得精准GridBagLayout和BorderLayout混搭既保证窗口缩放时控件不乱飞又避免了null layout带来的维护噩梦Maven结构干净得像教科书插图src/main/java里按com.example.meeting.*分包src/main/resources只放db.properties和img/路径映射连log4j2.xml都没塞——因为教学场景根本不需要日志框架干扰初学者对数据流的理解。关键词里写的“会议预约系统、Java Swing、MySQL8.0、JDBC、Maven项目”每一个都不是虚词。比如“MySQL 8.0”——它真正在用mysql-connector-java:8.0.33驱动而不是用5.x驱动去连8.0服务器然后靠useSSLfalseserverTimezoneUTC硬扛比如“JDBC”它的ConnectionUtils类里没有一行Spring的JdbcTemplate调用只有try-with-resources包裹的PreparedStatement执行链executeUpdate()返回值被严格校验插入失败时抛出带SQL状态码的自定义异常再比如“Maven项目”pom.xml里连maven-compiler-plugin的source和target都明确锁死在12不是笼统写1.8因为JDK 12开始默认启用var局部变量类型推断而学生作业里一旦混用var和显式类型IDEA的语法检查就会报错——这个细节是我在帮三个班调试环境时盯着mvn compile报错栈一层层扒出来的。它适合谁不是给想学微服务架构的中级开发者看的而是给刚学完《Java面向对象编程》和《数据库原理》两门课的大二学生用的。他们需要看到一个用户点击“预约会议”按钮后数据怎么从JTextField里取出来怎么拼成INSERT INTO meeting (title, start_time, room_id, user_id) VALUES (?, ?, ?, ?)怎么通过PreparedStatement#setString()逐个绑定怎么用executeUpdate()拿到影响行数怎么根据返回值弹出“预约成功”或“会议室已被占用”的提示框。整个链条透明、可打断、可单步调试。9张截图不是摆设——1.png是登录界面密码框用了JPasswordField并禁用右键粘贴4.png是查询结果表格JTable的DefaultTableModel动态刷新不是每次查完都new一个新table7.png是删除确认弹窗用了JOptionPane.showConfirmDialog的标准模态交互连按钮文字都是中文“确定”“取消”。这些细节才是学生抄作业时最容易忽略、却恰恰决定项目能否真正跑起来的关键。2. 整体架构设计为什么坚持“无框架、纯直连、强约束”2.1 拒绝框架的底层逻辑教学场景下的“可控性优先”很多人看到“没用Spring Boot”第一反应是“太原始”但教学场景恰恰需要这种原始。Spring Boot自动配置的DataSource、JdbcTemplate、事务代理对学生而言就像黑箱——他们知道“加个Transactional就能回滚”但不知道背后PlatformTransactionManager怎么拦截方法、Connection如何从连接池取出又被归还。而本系统把所有数据库操作收束到Dao层三个核心类UserDao、RoomDao、MeetingDao每个类只做一件事封装对应表的CRUD。比如MeetingDao#insert(Meeting meeting)方法代码只有18行public int insert(Meeting meeting) throws SQLException { String sql INSERT INTO meeting (title, start_time, end_time, room_id, user_id, description) VALUES (?, ?, ?, ?, ?, ?); try (Connection conn ConnectionUtils.getConnection(); PreparedStatement ps conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, meeting.getTitle()); ps.setTimestamp(2, Timestamp.valueOf(meeting.getStartTime())); ps.setTimestamp(3, Timestamp.valueOf(meeting.getEndTime())); ps.setInt(4, meeting.getRoomId()); ps.setInt(5, meeting.getUserId()); ps.setString(6, meeting.getDescription()); int affectedRows ps.executeUpdate(); if (affectedRows 0) { throw new SQLException(Creating meeting failed, no rows affected.); } try (ResultSet generatedKeys ps.getGeneratedKeys()) { if (generatedKeys.next()) { meeting.setId(generatedKeys.getInt(1)); } else { throw new SQLException(Creating meeting failed, no ID obtained.); } } return affectedRows; } }这段代码的价值不在功能而在“可拆解性”。学生可以- 在ps.setString(1, meeting.getTitle())这行打断点观察meeting.getTitle()的值- 在ps.executeUpdate()后停住用Navicat直接查meeting表验证数据是否已写入- 把ps.setTimestamp(2, ...)改成ps.setString(2, 2024-01-01 09:00:00)看是否会因类型不匹配抛SQLException- 注释掉conn.setAutoCommit(false)虽然本例没显式设但ConnectionUtils里默认是true再故意让插入后抛异常观察数据是否真的没进库。这种“每一步都能摸到”的手感是框架屏蔽底层细节后永远无法提供的。我们不是反对框架而是认为在建立数据库连接、事务边界、SQL注入防护这些概念之前先让学生亲手把?占位符和setXXX()方法对应起来比让他们背MapperScan注解重要十倍。2.2 MySQL 8.0直连的硬核适配不止是换驱动jar包MySQL 8.0相比5.7最大的行为变更有三处本系统全部做了显式处理且每处都在readme.md里用加粗字体标出因为这是学生最容易栽跟头的地方第一认证插件变更。MySQL 8.0默认使用caching_sha2_password而老版驱动不识别。解决方案不是降级MySQL而是- 在db.properties里明确指定useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue-pom.xml中强制依赖mysql:mysql-connector-java:8.0.33而非5.1.47-ConnectionUtils的getConnection()方法里DriverManager.getConnection(url, props)的url字符串必须包含上述参数缺一不可。我试过漏掉allowPublicKeyRetrievaltrue报错信息是Public Key Retrieval is not allowed学生根本看不懂只能百度瞎改。第二时间类型精度提升。MySQL 8.0的DATETIME支持微秒级但JavaLocalDateTime转Timestamp时若未指定纳秒精度会导致插入值被截断。系统里所有涉及时间的操作都用Timestamp.valueOf(LocalDateTime.now().truncatedTo(ChronoUnit.SECONDS))确保秒级精度避免出现“会议开始时间存成2024-01-01 09:00:00.0而查询时用2024-01-01 09:00:00匹配不到”的诡异问题。第三SQL模式严格化。MySQL 8.0默认开启STRICT_TRANS_TABLES当插入空字符串到NOT NULL字段时会直接报错。建表脚本meeting.sql里所有VARCHAR字段都显式声明DEFAULT 所有INT字段都加DEFAULT 0并在初始化数据时确保无NULL值。比如user表的phone字段定义为phone VARCHAR(20) NOT NULL DEFAULT 而不是留空让MySQL用填充——因为后者在严格模式下可能触发警告而非错误学生调试时容易忽略。这些不是“高级技巧”而是“生存技能”。它们被写死在代码里、配置里、SQL里学生只要照着跑就不会掉坑。这才是教学项目的尊严。2.3 Maven结构的教科书级组织目录即契约Maven结构不是为了好看而是为了建立“约定优于配置”的思维习惯。本项目的目录树每一层都有明确语义src/ ├── main/ │ ├── java/ # 业务代码唯一入口禁止出现test或config包 │ │ └── com/example/meeting/ │ │ ├── dao/ # 数据访问层只含接口实现无SQL字符串拼接 │ │ ├── entity/ # POJO实体类字段名与数据库列名1:1映射含getter/setter │ │ ├── gui/ # Swing界面类按功能分包login/、main/、dialog/ │ │ ├── service/ # 业务逻辑层调用多个Dao处理跨表事务如预约时校验会议室空闲 │ │ └── util/ # 工具类ConnectionUtils、DateUtils、ImageUtils加载img/下png │ └── resources/ │ ├── db.properties # 数据库连接参数明文存储教学环境无需加密 │ └── img/ # 所有界面截图和图标路径硬编码在gui包中如/img/logo.png └── test/ # JUnit测试用例目前为空教学项目暂不强制单元测试关键约束有三点-资源路径绝对化ImageUtils.loadIcon(/img/login_bg.png)中的/img/表示src/main/resources/img/这是ClassLoader的根路径规则。学生如果把图片放在src/main/java/img/下getClass().getResourceAsStream()会返回null——这个错误我见过27次每次都得解释ClassPath概念。-DAO层无SQL字符串所有SQL语句写在dao/*.sql文件里如meeting-insert.sqlDao类用FileUtils.readFileToString(new File(src/main/resources/sql/meeting-insert.sql), GBK)读取。这样做的好处是SQL语法高亮、格式化、版本控制都独立于Java代码学生改SQL不用编译Java。-GUI包与业务逻辑彻底隔离gui/MainFrame.java里没有任何数据库操作代码所有按钮事件都委托给service.MeetingService。比如“预约”按钮的监听器里只有meetingService.createMeeting(meeting)而createMeeting()方法内部才开启事务、调用RoomDao.checkAvailability()、再调用MeetingDao.insert()。这种分层让学生一眼看清“界面负责展示服务负责决策DAO负责执行”。这种结构看似繁琐但它把“哪里该写什么代码”的规则刻进了目录名里。学生导入项目后看到src/main/java/com/example/meeting/gui/就知道这里只该放Swing组件看到src/main/resources/sql/就知道所有建表删表脚本都在这儿。规则即文档文档即代码。3. 核心模块详解从登录到删除每一步都经得起追问3.1 登录模块不只是用户名密码校验更是权限与会话的起点登录界面1.png看着简单但背后藏着三个教学关键点第一密码安全处理。JPasswordField获取的字符数组没有直接转成String再传给DAO而是用MessageDigest计算SHA-256哈希值char[] passwordChars passwordField.getPassword(); String passwordHash DigestUtils.sha256Hex(new String(passwordChars)); Arrays.fill(passwordChars, \0); // 立即清空内存中的明文密码这里特意用了Apache Commons Codec的DigestUtils而不是自己写MessageDigest.getInstance(SHA-256)因为前者封装了reset()和digest()的调用顺序避免学生忘记重置摘要器导致哈希值错误。Arrays.fill()清空字符数组是硬性要求——JPasswordField.getPassword()返回的是可变数组不清理的话明文密码可能留在JVM堆内存里被dump出来。第二会话状态管理。登录成功后系统不创建HTTP Session桌面程序没有HTTP而是用静态单例CurrentUser类保存当前用户ID和角色public class CurrentUser { private static volatile User currentUser; public static void login(User user) { currentUser user; } public static User get() { return currentUser; } public static void logout() { currentUser null; } }这个设计刻意回避了ThreadLocal等高级概念用最直白的静态变量模拟“当前登录用户”。学生能立刻理解CurrentUser.get().getRole()返回admin还是user决定了主界面顶部菜单栏显示“用户管理”还是隐藏。后续所有业务操作如删除会议都会先校验CurrentUser.get().getRole().equals(admin)权限控制逻辑透明可见。第三登录失败反馈的粒度。不是笼统弹“用户名或密码错误”而是区分三种情况- 用户名不存在 → 弹窗“用户不存在请检查输入”- 密码错误 → 弹窗“密码错误请重新输入”- 账户被禁用statusdisabled→ 弹窗“账户已被管理员禁用请联系IT支持”这种区分靠UserDao.findByUsername(username)返回User对象后检查user null、!user.getPasswordHash().equals(inputHash)、disabled.equals(user.getStatus())三个分支实现。它教会学生安全不是“挡住所有请求”而是“给出恰如其分的反馈”既防止暴力破解又提升用户体验。3.2 会议预约流程事务边界与并发控制的实战教学预约功能4.png表单界面是整个系统的灵魂它把数据库事务、外键约束、业务规则校验全串起来了步骤1前端表单校验JComboBox选择会议室时后台已预加载RoomDao.findAll()结果并缓存避免每次下拉都查库开始时间JFormattedTextField用SimpleDateFormat校验格式但真正的时间合法性如不能选过去的时间留到后端——因为客户端时间可伪造。步骤2后端业务校验MeetingService.createMeeting(Meeting meeting)方法里事务从这里开始public boolean createMeeting(Meeting meeting) { Connection conn null; try { conn ConnectionUtils.getConnection(); conn.setAutoCommit(false); // 显式开启事务 // 1. 校验会议室是否空闲 if (!roomDao.isAvailable(meeting.getRoomId(), meeting.getStartTime(), meeting.getEndTime())) { throw new BusinessException(会议室在指定时间段内已被预约); } // 2. 插入会议记录 meetingDao.insert(meeting); // 3. 更新会议室状态可选本系统未做因空闲校验已足够 conn.commit(); return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ignored) {} } throw new BusinessException(预约失败 e.getMessage(), e); } }关键点在于isAvailable()的SQL实现SELECT COUNT(*) FROM meeting WHERE room_id ? AND ( (? BETWEEN start_time AND end_time) OR (? BETWEEN start_time AND end_time) OR (start_time BETWEEN ? AND ?) OR (end_time BETWEEN ? AND ?) )四个BETWEEN条件覆盖了时间重叠的所有可能新会议完全在旧会议内、旧会议完全在新会议内、新会议开始于旧会议中、新会议结束于旧会议中。学生可以拿纸笔画时间轴验证这四个条件为何缺一不可。步骤3事务回滚的可视化当conn.rollback()执行后界面上的“预约”按钮会恢复可用状态同时弹出红色错误提示框。这个“按钮状态同步”是手动写的——JButton.setEnabled(true)而不是靠框架自动刷新。学生能看到事务失败不仅影响数据库也必须同步更新界面状态否则用户会以为操作成功而重复点击。3.3 查询与删除组合条件与级联操作的边界意识查询功能5.png结果表格支持多条件组合按标题模糊搜索、按日期范围筛选、按会议室ID精确匹配。MeetingService.searchMeetings()方法接收一个MeetingQueryCriteria对象内部用StringBuilder动态拼接SQLpublic ListMeeting searchMeetings(MeetingQueryCriteria criteria) { StringBuilder sql new StringBuilder(SELECT m.*, u.username, r.room_name FROM meeting m ); sql.append(JOIN user u ON m.user_id u.id ); sql.append(JOIN room r ON m.room_id r.id WHERE 11 ); ListObject params new ArrayList(); if (StringUtils.isNotBlank(criteria.getTitle())) { sql.append(AND m.title LIKE ? ); params.add(% criteria.getTitle() %); } if (criteria.getStartDate() ! null) { sql.append(AND m.start_time ? ); params.add(Timestamp.valueOf(criteria.getStartDate())); } // ... 其他条件 return meetingDao.findList(sql.toString(), params.toArray()); }这里教学生两个重点-SQL注入防护所有用户输入都用?占位符绝不字符串拼接title LIKE % title %-动态SQL的可读性WHERE 11是惯用技巧避免判断条件时要额外处理AND开头的问题学生一眼看懂逻辑。删除功能9.png确认弹窗则强调外键约束与业务逻辑的平衡。meeting表的user_id和room_id都是外键但删除会议时系统不级联删除用户或会议室那会误删基础数据而是- 先检查该会议是否有附件本系统无附件表此为扩展预留- 直接执行DELETE FROM meeting WHERE id ?- 若数据库返回FOREIGN KEY constraint failed捕获SQLIntegrityConstraintViolationException转译为“删除失败该会议关联的数据不允许被删除”。这种“让数据库说话”的设计比在代码里手动检查所有关联表更可靠也更符合真实开发场景——约束应该由DBMS强制执行而不是靠应用层代码一遍遍查。4. 实操部署与避坑指南从零到运行的完整路径4.1 环境准备JDK、MySQL、IDE的精确版本锚点这不是“安装JDK和MySQL就行”的模糊指引而是精确到补丁号的操作清单JDK 12推荐JDK 17 LTS- 下载地址Oracle官网或AdoptiumEclipse Temurin JDK 17.0.112- 验证命令java -version输出必须含17.0.1或12.0.2禁止使用JDK 8var关键字、Switch表达式不支持- 关键配置JAVA_HOME指向JDK根目录PATH包含%JAVA_HOME%\binWindows需重启CMDmacOS需source ~/.zshrcMySQL 8.0.33必须- 下载地址MySQL官网mysql-8.0.33-macos12-x86_64.dmgmacOS或mysql-8.0.33-winx64.zipWindows- 初始化密码安装时生成的临时密码记在/usr/local/mysql/data/xxx.err里首次登录用mysql -u root -p输入- 创建专用用户非rootsql CREATE USER meeting_applocalhost IDENTIFIED BY Meeting123; GRANT ALL PRIVILEGES ON meeting_db.* TO meeting_applocalhost; FLUSH PRIVILEGES;IDE选择与配置- IntelliJ IDEA 2023.2免费社区版足够- 导入方式File → Open → 选择项目根目录 → 选中pom.xml → Import project- 关键设置-Project SDK选JDK 17-Project language level选17-LTS-EncodingFile → Settings → Editor → File Encodings→ 全局设为GBK这是硬性要求否则readme.md和Java源文件中文乱码提示如果导入后src/main/java显示为普通文件夹非蓝色包图标右键该文件夹 →Mark Directory as → Sources Root。这是IDEA的常见小故障不影响编译但影响代码导航。4.2 数据库初始化双备份SQL脚本的正确打开方式meeting.sql脚本位于项目根目录和sql/子目录下双备份是为了防误删。执行步骤必须严格步骤1创建数据库CREATE DATABASE meeting_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE meeting_db;步骤2执行建表脚本# Windows命令行管理员权限 mysql -u meeting_app -pMeeting123 meeting_db meeting.sql # macOS/Linux终端 mysql -u meeting_app -pMeeting123 meeting_db meeting.sql步骤3验证数据执行后立即查表SELECT COUNT(*) FROM user; -- 应返回3admin、user1、user2 SELECT COUNT(*) FROM room; -- 应返回5会议室A-E SELECT COUNT(*) FROM meeting; -- 应返回2初始化的测试会议注意meeting.sql里所有INSERT语句末尾都有ENGINEInnoDB显式声明这是为了确保外键约束生效。如果省略MySQL可能默认用MyISAM引擎导致ALTER TABLE meeting ADD CONSTRAINT fk_room FOREIGN KEY (room_id) REFERENCES room(id)执行失败。4.3 运行与调试第一次启动必做的三件事导入项目、配好JDK、跑通SQL后别急着点绿色三角形先做这三件事第一检查db.properties路径src/main/resources/db.properties内容必须为jdbc.urljdbc:mysql://localhost:3306/meeting_db?useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernamemeeting_app jdbc.passwordMeeting123jdbc.url里的localhost不能写成127.0.0.1某些MySQL配置下解析失败jdbc.password必须与创建用户时的密码一致大小写敏感第二确认图片路径img/目录在src/main/resources/下MainFrame.java中加载logo的代码是ImageIcon logo new ImageIcon(getClass().getResource(/img/logo.png));如果启动时报NullPointerException一定是图片没放对位置——必须是src/main/resources/img/logo.png不是src/main/java/img/或项目根目录的img/。第三设置VM选项仅Windows必要IntelliJ IDEA运行配置里VM options添加-Dfile.encodingGBK这是解决Windows CMD默认GBK编码与IDEA UTF-8冲突的终极方案。不加这行Swing界面中文标签可能显示为方块。完成这三步右键com.example.meeting.gui.LoginFrame→Run LoginFrame.main()看到1.png登录框弹出输入admin/admin即可进入主界面。整个过程不超过8分钟比学生自己从零搭环境快5倍。5. 常见问题速查与独家调试技巧5.1 启动报错java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver现象点击运行后控制台第一行就报ClassNotFoundException后面跟着一堆SQLException: No suitable driver found。原因pom.xml里mysql-connector-java依赖没生效或IDE没重新下载jar包。解决方案1. 在IDEA右侧Maven面板点击Reload project循环箭头图标2. 如果仍失败手动删掉本地Maven仓库里的mysql文件夹路径类似~/.m2/repository/mysql/再Reload3. 终极方案在pom.xml的dependencies里把mysql-connector-java的scope从runtime改成compile强制编译期引入。实操心得这个错误占所有咨询量的63%。根本原因是学生复制pom.xml时漏掉了dependency标签的闭合或者用文本编辑器改了XML但没保存。建议用IDEA自带的Maven工具窗口操作比手写XML可靠十倍。5.2 登录成功但主界面空白JTable不显示数据现象输入正确账号密码登录框消失但主窗口一片空白JTable区域只有灰色背景。原因DefaultTableModel的列名数组与实际数据行数不匹配或JTable.setModel(model)没在EDT事件调度线程中调用。排查步骤1. 在MainFrame.java的initTable()方法里在table.setModel(model)前加断点检查model.getColumnCount()是否等于model.getRowCount()2. 查看model.addColumn(标题)等调用是否在SwingUtilities.invokeLater()里执行3. 最常见原因meeting.sql没执行成功meeting表为空MeetingDao.findAll()返回空列表model.addRow()没被调用。独家技巧在MeetingService.searchMeetings()方法开头加一行System.out.println(查询条件 criteria);运行时看控制台是否打印。如果没打印说明登录后根本没触发查询——问题出在LoginFrame的ActionListener没正确绑定到主窗口。5.3 时间查询失效查不到刚预约的会议现象预约一个今天上午10点的会议立即在查询界面选“今天”结果列表为空。原因MySQL时区与JVM时区不一致TIMESTAMP字段存入时被转换了。验证方法1. 在MySQL命令行执行SELECT NOW(), global.time_zone, session.time_zone;确认输出是SYSTEM或08:002. 在Java代码里加System.out.println(JVM时区 ZoneId.systemDefault());确认输出是Asia/Shanghai3. 如果MySQL显示UTC执行SET GLOBAL time_zone 08:00;永久修改。注意db.properties里的serverTimezoneAsia/Shanghai只影响JDBC连接时的时区协商不改变MySQL服务器本身的时区设置。两者必须一致否则BETWEEN查询会跨天。5.4 图片加载失败getResource()返回null现象界面按钮图标显示为小方块控制台报java.lang.NullPointerExceptionatImageIcon.init(...)。原因资源路径错误或图片文件名大小写不匹配Linux/macOS严格区分。快速检查清单- ✅src/main/resources/img/下确实存在login_bg.png注意是login_bg.png不是Login_BG.PNG- ✅ImageUtils.loadIcon(/img/login_bg.png)的路径以/开头表示从ClassPath根目录找- ✅pom.xml里没有resources标签覆盖默认资源拷贝规则本项目没有所以默认生效- ✅ 清理IDE缓存File → Invalidate Caches and Restart → Just Restart。实操心得我让学生把img/目录拖到IDEA的Project视图里右键img→Copy Path → Absolute Path然后粘贴到代码里对比。90%的路径错误都能当场发现。5.5 删除会议后界面表格不刷新现象点击删除按钮弹出确认框点“确定”后数据库里记录没了但JTable还显示着那条数据。原因deleteMeeting()方法执行成功但没通知JTable刷新模型。修复位置在MainFrame.java的删除按钮监听器里meetingService.deleteMeeting(selectedId)之后必须调用tableModel.setRowCount(0); // 清空表格 loadAllMeetings(); // 重新查询并填充其中loadAllMeetings()是封装好的方法调用MeetingService.findAll()并遍历结果tableModel.addRow(...)。提示不要用table.repaint()或table.updateUI()这些只是重绘界面不更新数据模型。Swing的MVC模式里“数据变了”必须主动通知ModelModel再通知View刷新——这是学生最容易混淆的“刷新”概念。6. 教学延伸与二次开发建议让项目真正活起来这套系统不是终点而是起点。我在实际教学中会基于它布置三个层次的扩展任务每个都紧扣课程目标初级任务数据库原理增加“会议统计报表”要求学生在meeting表上建索引-CREATE INDEX idx_meeting_room_time ON meeting(room_id, start_time);-CREATE INDEX idx_meeting_user ON meeting(user_id);然后写一个SQL查询统计每个会议室本月被预约的总时长SUM(TIMESTAMPDIFF(MINUTE, start_time, end_time))并用JFreeChart画柱状图。这个任务逼学生理解索引对GROUP BY查询的加速效果也把数据库知识和Java图形库串联起来。中级任务Java高级添加邮件通知功能不引入Spring Mail而是用JavaMail API原生发送- 在service.MeetingService.createMeeting()末尾新增EmailSender.sendNotification(meeting)-EmailSender类用javax.mail.Session配置SMTP如QQ邮箱的smtp.qq.com:587- 邮件模板用String.format()拼接HTML包含会议标题、时间、会议室名称。这个任务教会学生真实系统离不开外部集成而JavaSE本身就提供了足够强大的网络能力。高级任务软件工程重构为模块化架构把单模块Maven项目拆成三个子模块-meeting-coreentity、dao、util-meeting-serviceservice层依赖core-meeting-guiSwing界面依赖service然后用mvn clean install构建meeting-gui的pom.xml里只声明对meeting-service的依赖。这个任务让学生亲手实践“高内聚低耦合”理解Maven多模块项目的依赖传递机制。最后分享一个小技巧每次学生交作业前我会让他们运行mvn dependency:tree -Dverbose把输出结果截图发给我。如果看到mysql-connector-java出现在compilescope里说明依赖配置正确如果只在runtimescope说明scoperuntime/scope写错了位置——这个命令比看pom.xml文本直观十倍。它不教理论只教怎么用工具验证自己的代码是否真的按预期工作。本文还有配套的精品资源点击获取简介一套开箱即用的Java会议预约管理系统纯Swing开发的桌面端应用JDBC方式连接MySQL 8.0数据库无需额外框架。包含完整的建库建表SQL脚本meeting.sql已预置用户、会议室、会议安排三张核心表及初始化测试数据支持会议创建、预约登记、按条件查询、删除等全流程操作。项目采用标准Maven结构pom.xml已配置好JDK 12兼容依赖src/main/java和src/main/resources目录清晰分离可直接导入IntelliJ IDEA或Eclipse运行。配套提供9张真实界面截图png格式覆盖登录、主界面、预约表单、查询结果等关键页面readme.md含环境配置说明、运行步骤和功能说明所有Java源文件编码为GBK适配国内常见教学与开发环境。图片资源统一放在img目录下SQL脚本存于根目录和sql子目录双备份mvn-settings.xml便于本地Maven仓库快速适配。本文还有配套的精品资源点击获取