☰
Swing+JDBC酒店管理系统源码解析与二次开发实战
2026/10/18 18:17:59 网站建设 项目流程

简介:一套基于Java Swing与MySQL开发的酒店管理系统项目源码,面向正在完成课程设计、毕业设计,或希望掌握桌面窗体应用与数据库联动开发的读者,能够帮助搭建涵盖客房管理、预订管理、客户信息维护和账单结算等多个环节的后台系统。资源共294个文件,内含54个java源文件、59个class编译文件、135个gif操作截图、17个png图标、8个db数据库文件、2个sql数据库脚本、5个doc说明文档和1个mp4运行演示视频,压缩包约72.05MB,目录结构清晰,便于按模块定位所需内容。已有2180人学习/下载,适合需要快速上手Swing窗体开发,并理解JDBC、DAO模式等数据库编程技巧的开发者。内容充分展示JFrame、JTable等组件的界面组织与事件处理流程,也覆盖JDBC连接MySQL执行数据增删改查、DAO层封装以及异常处理等关键知识点。借助附带的运行录屏和界面截图,可以直观对照每个页面的交互效果与代码实现,便于二次开发和扩展成完整的酒店运营管理系统。

1. 酒店管理系统:一个能直接跑的 Swing 桌面应用

刚接手这套源码的时候,我以为是那种只有登录页和几个空表格的课设半成品。翻完才发现,散客登记、团队入住、预订管理、退房结账这些业务全都能走通,界面用 Swing 搭的,数据落 MySQL,属于那种“打开就能跑、跑起来就能点”的完整桌面应用。如果你正在学 Java 图形界面开发,或者需要一个能二次开发的酒店后台管理底座,这套代码值得下载下来对照着看。它能帮你把 Swing 组件、事件监听、JDBC 数据库操作这几条线一次性串起来。

2. 系统功能与代码结构:从主窗体到业务子模块

2.1 主窗体 HotelFrame:系统的总入口

整套系统的主界面是 HotelFrame.class 对应的窗体。酒店的日常操作都从这个窗体进入,左侧是功能导航,右侧是内容面板,典型的 Swing 桌面应用布局方式。从源码的 class 文件名可以推测出,系统按业务拆成了散客(Individual)、团队(Team)、预订(Engage)、查询(Query)几个独立模块,每个模块在 JFrame 或 JPanel 基础上封装了自己的交互界面。

窗口布局的常见做法是使用 BorderLayout 作为主布局,顶部放 JMenuBar 菜单栏,左侧放 JButton 导航按钮,中间放 JPanel 作为内容切换区域。点击左侧按钮时,通过 CardLayout 切换不同的业务面板,这样整个窗口只需要创建一次,面板切换时也不需要频繁重新实例化对象。

2.2 散客与团队:Individual 和 Team 两类入住流程

Individual.class 处理散客入住。散客入住的特点是“一个房间、一个人、短住”,所以界面字段少,操作路径短,通常只需要选房号、填姓名、录证件号、收押金,然后点击入住即可。Team.class 处理团队入住,和散客的差别在于:一次开多间房、房间可能在不同楼层、需要批量登记成员信息。

团队入住在界面上比散客多了两个关键操作:批量选房和团队信息维护。批量选房用 JTable 展示可售房列表,支持多行选中,一次性为多间房生成入住记录。团队信息单独存表,预订记录通过团队编号关联。这种按“业务对象”拆分界面的方式,好处是代码隔离干净,散客的逻辑改动不会影响团队模块,这一点在二次开发时特别省心——不像单类堆功能的项目,改一行都可能炸出连锁问题。

2.3 预订与查询:Engage、EngageInfo、Query 的功能划分

Engage.class 负责预订操作,EngageInfo.class 负责预订信息的核对与展示,Query.class 提供综合查询功能。预订的流程是:先查可售房型,再填客户联系信息,选定预计入住和离店日期,系统根据房价生成预订记录。预订时异常处理的一个关键点是日期校验——如果入店日期晚于离店日期,系统会通过 JOptionPane 弹窗提示并阻止保存。

查询模块 Query 是我觉得整套系统里最有参考价值的一块。它把散客入住记录、团队入住记录、预订信息、当前房间状态整合在一个查询界面里,通过下拉框选择查询条件,JTable 展示结果。查询条件与 SQL 条件之间是手动拼接的,这意味着你需要自己处理 SQL 注入风险。实践里我一般会在底层封装一个查询参数对象,传入的条件用 PreparedStatement 占位符绑定,而不是直接拼字符串进 SQL。

3. 数据访问与核心业务:JDBC、DAO 与事件监听怎么协同

3.1 数据库连接:JDBC 驱动的加载与连接管理

酒店管理系统在 Java 端通过 JDBC 连接 MySQL。传统的 JDBC 连接方式写法比较固定:先加载驱动类,再用 DriverManager 获取连接。这套源码里应该有一个独立的工具类负责这件事,后续的 DAO 类都从它拿 Connection 对象。

import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBHelper { private static final String DRIVER = "com.mysql.jdbc.Driver"; private static final String URL = "jdbc:mysql://localhost:3306/hotel_db?useSSL=false&characterEncoding=utf8"; private static final String USER = "root"; private static final String PASSWORD = "123456"; static { try { Class.forName(DRIVER); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(URL, USER, PASSWORD); } }

这段代码里最关键的是Class.forName(DRIVER)这一步。它把 MySQL 驱动类加载进 JVM,DriverManager 才能识别对应的 JDBC URL。URL 里的useSSL=false参数在本地开发时必须保留,否则高版本 MySQL 会有 SSL 连接警告;characterEncoding=utf8确保中文不乱码,这是这类中文桌面系统最容易翻车的地方。数据库名、账号密码都集中在常量上,方便换库时统一修改。

3.2 DAO 层设计:预订信息的增删改查

DAO 模式在这套系统里的体现,是每个业务模块都有对应的数据访问类。以预订功能为例,新增一条预订记录的方法可以这样理解:

public int insertEngage(Engage engage) { String sql = "INSERT INTO engage_info (room_id, customer_name, phone, arrive_date, leave_date, order_time) VALUES (?, ?, ?, ?, ?, NOW())"; try (Connection conn = DBHelper.getConnection(); PreparedStatement ps = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { ps.setString(1, engage.getRoomId()); ps.setString(2, engage.getCustomerName()); ps.setString(3, engage.getPhone()); ps.setString(4, engage.getArriveDate()); ps.setString(5, engage.getLeaveDate()); ps.executeUpdate(); ResultSet rs = ps.getGeneratedKeys(); if (rs.next()) { return rs.getInt(1); } } catch (SQLException e) { e.printStackTrace(); } return 0; }

这个 DAO 方法的核心是两件事:用 PreparedStatement 占位符绑定参数,解决拼 SQL 的注入风险;用RETURN_GENERATED_KEYS把数据库自增的主键取回来,方便后续关联其他表。实际开发中,try-with-resources可以自动关闭连接和状态集,比手动在 finally 里关要省心得多。

3.3 退房结账:一次跨越两张表的业务逻辑

退房结账是酒店管理系统里逻辑最完整的业务。以散客退房为例,操作分为三步:根据房号查入住记录、计算总费用(房价乘以天数)、删除入住记录并更新房间状态。整个流程里,费用计算用long类型避免精度问题,时间差用Date对象计算。

public double calTotalPrice(Date arriveDate, Date leaveDate, double pricePerDay) { long diff = leaveDate.getTime() - arriveDate.getTime(); int days = (int) (diff / (1000 * 60 * 60 * 24)); if (days <= 0) { days = 1; } return days * pricePerDay; }

这里的days = 1是一个兜底逻辑:如果当天入住当天退房,时间差算出来是 0,但房费按一天计算。这是酒店行业的计价习惯,写代码时容易忽略。注意这里的参数 arriveDate、leaveDate 是 java.util.Date 类型,getTime() 返回毫秒数,底层用 long 做差值不会溢出,但如果数据从 ResultSet 直接取出,通常已经过 JDBC 转换。

4. 运行环境与踩坑排查:从 MySQL 到启动参数的完整记录

4.1 环境准备:JDK、MySQL 与导入工程

要让这套源码跑起来,需要准备 JDK 8 或更高版本(Swing API 在 Java SE 里,不用额外装库)、MySQL 5.7 或 8.x、以及一个 Java IDE。IDE 推荐用某知名开发工具,因为它对 Swing 的可视化预览支持比较成熟,右键 JFrame 可以直接看界面效果。

先把源码解压后作为已有项目导入 IDE。如果导入的是 class 文件而不是 java 文件,那就需要反编译工具配合阅读。项目里多数情况会有 src 目录放着 .java 源文件,class 文件只是编译产物,优先找源文件,找不到再用反编译工具打开 class 对照着看逻辑。

导入后的第一件事是改数据库连接配置。检查 DBHelper 类里的账号密码是否和本地 MySQL 一致,另外需要在 MySQL 里建一个hotel_db数据库并导入项目提供的 SQL 脚本。如果项目里没有现成的 SQL 脚本,需要根据 DAO 层表结构手工建表——这是个体力活,但建完后对表结构会特别清楚。

4.2 常见启动问题:驱动、时区、端口

4.2.1 报 “ClassNotFoundException: com.mysql.jdbc.Driver”

原因:IDE 项目里没有引入 mysql-connector-java 的 jar 包。

解决:从本机 Maven 仓库或官网下载 mysql-connector-java(注意用 5.1.x 还是 8.0.x 取决于驱动类名)。下载后放到项目根目录下的 lib 文件夹,然后在 IDE 里把它添加到项目构建路径。

4.2.2 中文显示成问号或者乱码

原因:MySQL 连接 URL 里没有指定字符集,或者表本身是 latin1 编码。

解决:在 JDBC URL 里加上characterEncoding=utf8参数,同时确保建表语句带DEFAULT CHARSET=utf8mb4。改完后重启程序再试。

4.2.3 连接 MySQL 报 “Public Key Retrieval is not allowed”

原因:高版本 MySQL(8.x)默认使用 caching_sha2_password 认证插件,JDBC 驱动版本较旧或参数缺失。

解决:改驱动包为 mysql-connector-java 8.0.16 以上,并在 URL 后追加allowPublicKeyRetrieval=true。

4.2.4 窗口打开后界面错位,按钮显示不全

原因:JFrame 没有调用setSize或pack()之前没有设置合适的布局管理器。

解决:在 JFrame 构造函数里设置布局管理器,并显式调用setSize(1024, 768)。如果是面板切换类,检查 CardLayout 是否给每个子面板设置了setPreferredSize。

这些坑每一个我都实际踩过,尤其是字符集那个问题,刚接项目时没加 utf8 参数,整天看到一堆问号,半天才排查出来。后来每次搭这类环境都先检查连接 URL 的编码参数再开始写业务代码。

5. 二次开发:给系统加上会员折扣功能

这套酒店管理系统的可扩展性不差,模块间耦合度较低。真要说从哪下手,建议第一个改造点是加一个“会员等级折扣”模块。这能贯穿从建表到界面再到结账逻辑的整个链路,而且改动范围可控,适合练手摸清架构。

5.1 先加会员表和等级字段

在数据库里新增一张会员表,然后给客户表加一个会员等级字段。

CREATE TABLE vip_level ( id INT PRIMARY KEY AUTO_INCREMENT, level_name VARCHAR(20), discount DECIMAL(4, 2) DEFAULT 1.00 ); INSERT INTO vip_level VALUES (1, '普通会员', 0.95), (2, '银卡会员', 0.90), (3, '金卡会员', 0.85);

折扣字段用DECIMAL(4,2)而不是 float,原因很简单:金额计算用浮点数会出现 0.8500000000001 这类问题。Java 端读取折扣值时也建议用BigDecimal而不是直接转 double。

5.2 在退房结账时接上折扣逻辑

改造 Individual 模块中的退房方法。核心思路是:根据客户手机号查出会员等级,再根据等级折扣调整结算金额。

public double calcWithDiscount(String phone, double total) { double discount = 1.00; String sql = "SELECT v.discount FROM vip_level v JOIN customer c ON v.id = c.level_id WHERE c.phone = ?"; try (Connection conn = DBHelper.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, phone); ResultSet rs = ps.executeQuery(); if (rs.next()) { discount = rs.getDouble("discount"); } } catch (SQLException e) { e.printStackTrace(); } return BigDecimal.valueOf(total) .multiply(BigDecimal.valueOf(discount)) .setScale(2, RoundingMode.HALF_UP) .doubleValue(); }

注意看这里有两个细致处理:一个是用 BigDecimal 做乘法,避免浮点误差;另一个是setScale(2, RoundingMode.HALF_UP)做半进位舍入,满足金额计算场景。业务侧如果还要再精细,可以把折扣计算独立成一个工具类,单元测试时直接传金额和等级验证结果。

5.3 界面层加上会员等级下拉框

在散客登记面板里增加一个 JComboBox,数据从 vip_level 表动态加载。这个改动点正好能用上系统原有的 DAO 模式,写一个VipDao类负责查等级列表。下拉框选中事件触发后把折扣值暂存,退房时读取。

变更结束后用一组最基础的数据做回归:一间 100 元的房间住 2 晚,没有会员应收 200 元,金卡会员应收 170.00 元。验算通过后再提交代码,基本不会出岔子。从那以后我每完成一个改造,都会先用一组边界数据强制走一遍退房流程,确认金额、房间状态、表记录三处同时一致才收工。希望这个验证习惯和这套源码的拆解思路能帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询