
简介这是一份基于Java Swing开发、带GUI界面的酒店宾馆管理系统源码与数据库打包资源适合正在完成Java课程设计或毕业设计的同学直接参考与复用。系统覆盖酒店日常管理常见场景提供登录注册、房间管理、用户信息管理、酒店面板等模块界面以Swing组件实现交互直观。压缩包共370个文件总大小43.65MB主要包含64个Java源文件、131个class编译文件、SQL数据库脚本、运行所需jar依赖以及大量png、jpg界面素材与XML配置文件目录结构清晰便于导入IDE后快速理解项目脉络。资源已吸引433人学习下载描述强调代码完整下载即可直接运行无需额外修改调试。对需要高质量Java桌面端课设源码、想节省开发时间并希望完整体验酒店管理系统实现流程的读者而言这份资料能提供明确的参考价值。1. 酒店宾馆管理系统用 Java Swing 做 GUI一份课设源码值不值得花时间跑通做 Java 课程设计绕不开“酒店宾馆管理系统”这个题目用 Java Swing 做 GUI 界面、配 MySQL 数据库做增删改查是 95 分以上课设最常见的技术组合。你拿到的源码包里通常是一个 Eclipse 或 IDEA 工程、几张 MYSQL 建表脚本和一份说明文档但很多人下载后第一关就卡住JDBC 驱动没导入、数据库连接串写错、字符集乱码界面弹出来却是空数据。这篇笔记会从系统设计、数据库建模、代码跑通、核心功能拆解到课设答辩前的改造把这类 Swing 项目的完整落地路径讲清楚。适合正在做课设、准备答辩或者想快速复用一个宿舍酒店管理项目来应付实训的读者如果你刚学完 Java 基础照着这里的步骤走完一遍也能把 Swing 和 JDBC 的套路彻底理顺。2. 先看清楚系统长什么样Swing 界面分层与酒店业务的数据流2.1 从“MVC”到“分层”Swing 课的隐含设计要求很多课设源码之所以能拿高分不是因为界面画得好看而是包结构体现了分层的“设计意识”。常见做法是分成dao、entity、util、view或ui四个包。entity放Room、CustomerInfo、OrderInfo、User这些实体类字段和数据库表字段一一对应dao写 JDBC 操作每个表至少对应一个RoomDao、OrderDaoutil里放DbUtil负责获得连接以及像DateUtil、StringUtil这种工具类view放LoginFrame、MainFrame、RoomManagerPanel等界面类只负责把数据填充到表格和响应按钮事件。Swing 本身没有强制 MVC但课设评分时“层次清晰”通常是明确加分项。不带分层思维的人会把 SQL 直接写在actionPerformed里几百行代码堆在一个类中虽然功能能跑但一改需求就崩。我一般这样对应数据模型就是实体类视图就是JPanel和JDialog控制逻辑则在dao和对应的监听器中。从拿到源码包的第一步起你应该先打开src目录数一下包的数量如果只有两个包那大概率是初学者项目跑通容易、答辩时很难解释高并发或多条件查询。2.2 酒店宾馆管理系统的表结构五张核心表与关联关系酒店管理系统不管界面差别多大核心业务绕不开五张表用户表、房间表、客户表、订单表和订单明细表。用户表存前台登录账号字段至少是id、username、password、role房间表存房号、类型、价格、状态状态字段用0表示空闲、1表示已入住、2表示已预订客户表存身份证号、姓名、电话订单表是主表记录roomId、customerId、checkInDate、checkOutDate、totalPrice、status订单明细表对应商品消费比如客人要了矿泉水、泡面每项记录一条。先想清楚状态字段再去看源码里的status判断你才能在改代码时不翻车。常见的表结构脚本大概是这样的CREATE DATABASE IF NOT EXISTS hotel_db DEFAULT CHARSET utf8mb4; USE hotel_db; CREATE TABLE t_user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(32) NOT NULL, password VARCHAR(64) NOT NULL, role VARCHAR(16) DEFAULT 前台 ); CREATE TABLE t_room ( id INT PRIMARY KEY AUTO_INCREMENT, room_no VARCHAR(8) UNIQUE NOT NULL, room_type VARCHAR(16), price DECIMAL(10,2), status TINYINT DEFAULT 0 ); CREATE TABLE t_customer ( id INT PRIMARY KEY AUTO_INCREMENT, id_card VARCHAR(18) UNIQUE, name VARCHAR(32), phone VARCHAR(16) ); CREATE TABLE t_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) UNIQUE, room_id INT, customer_id INT, check_in DATETIME, check_out DATETIME, total_price DECIMAL(10,2), status TINYINT DEFAULT 0, FOREIGN KEY (room_id) REFERENCES t_room(id), FOREIGN KEY (customer_id) REFERENCES t_customer(id) );外键在课设里可有可无但加了外键能体现对数据库约束的理解。注意DECIMAL(10,2)表示金额不要用DOUBLE二分浮点在做金额累加时会出错。order_no用 Unique 约束通常用时间戳加随机数生成避免主键连续被人猜出业务量。如果把 R 语言的经验带进来你会觉得这很向量化但 Java 课设里直接用SimpleDateFormat拼字符串最省事。2.3 数据流前台操作到数据库增删改查的完整回路理论上一次开房操作的完整数据流是前台在RoomManagerPanel中点“开房”按钮弹出一个JDialog填写客户信息点确定后监听器调用CustomerDao.insert(customer)插入客户再查询或拿到客户的id、同时选定房间的id组装Order对象调用OrderDao.insert(order)接着调用RoomDao.updateStatus(roomId, 1)把房间改成已入住最后刷新表格roomTableModel.setDataVector(...)。这四步是线性顺序不是事务这是课设项目和大厂项目的最大差距——但 95 分课设一般不要求事务除非答辩老师问。所以你在看源码时要盯住的就三件事第一DAO 里每条 SQL 是否用PreparedStatement而不是Statement用字符串拼接 SQL 的项目一定会被问安全问题第二窗口关闭时是否调用了DbUtil.close()释放连接连接不关会导致 MySQL 报Too many connections第三表格刷新是否用了自定义TableModel。很多低分课设是通过重建JTable来实现刷新界面闪烁严重查看代码时一眼就能看出来。理解了这条数据流你就知道为什么必须有RoomDao和OrderDao两个类而不是把 SQL 都丢在按钮监听器里。3. 把源码包跑起来JDK、Navicat 建库和项目导入三步走3.1 环境版本怎么选JDK 8 MySQL 5.7 是课设项目的安全组合下载的课设源码大多写于三到五年前编写时基于 JDK 8、MySQL 5.7 或 5.8。现在本机装了 JDK 17 或 MySQL 8.x跑老项目的常见症状是Eclipse 里编译报错、JDBC 驱动类找不到、连接串报Communications link failure。原因不是代码坏了而是驱动 jar 没匹配。JDK 8 的com.mysql.jdbc.Driver在 MySQL 8 驱动中已经改成com.mysql.cj.jdbc.Driver。所以如果源码包的DbUtil里写着老驱动类名你最好先别急着改代码。我建议直接用 JDK 8 和 MySQL 5.7不仅兼容最好而且 32 位操作系统也能跑。JDK 8 安装后记得设置JAVA_HOME和PATH然后在命令行输入java -version验证MySQL 5.7 用安装版时在最后一步设置root密码编码选utf8。如果本机已经有 MySQL 8也不建议卸载可以保留两个实例或者用 Docker 单独跑一个 5.7 容器但这涉及到端口映射课设阶段能省则省。3.2 导入项目与配置 JDBC从 db.properties 到 DbUtil打开源码包后先找lib目录里的 jar 包。如果里面没有mysql-connector-java-x.x.x.jar那就自己在网上下载一个 5.1.49 版本这个版本对 JDK 8 和 MySQL 5.7 都友好放到lib下并在 IDE 中 Add to Build Path。然后是数据库连接配置有两种常见风格一种是单独的db.properties配置文件内容如下drivercom.mysql.jdbc.Driver urljdbc:mysql://localhost:3306/hotel_db?useUnicodetruecharacterEncodingutf8 usernameroot password123456另一种是把连接参数直接写在DbUtil类的静态代码块里。两种都正常但db.properties更符合课程设计规范。characterEncodingutf8是乱码问题的关键如果数据库本身是utf8mb4这里也要对应改成utf8mb4否则插入 emoji 或特殊字符会失败。useSSLfalse一般也会加上否则 MySQL 8 驱动会在连接时警告或报错。修改完配置后测试连接是否成功最快的方法是在 main 方法中调用DbUtil.getConnection()并打印连接对象。package com.hotel.util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.util.Properties; public class DbUtil { private static String driver; private static String url; private static String username; private static String password; static { try { InputStream is DbUtil.class.getClassLoader() .getResourceAsStream(db.properties); Properties props new Properties(); props.load(is); driver props.getProperty(driver); url props.getProperty(url); username props.getProperty(username); password props.getProperty(password); Class.forName(driver); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws Exception { return DriverManager.getConnection(url, username, password); } public static void close(Connection conn, java.sql.PreparedStatement ps, java.sql.ResultSet rs) { try { if (rs ! null) { rs.close(); } if (ps ! null) { ps.close(); } if (conn ! null) { conn.close(); } } catch (Exception e) { e.printStackTrace(); } } }Class.forName(driver)是在显式加载驱动类JDBC 4.0 之后可以省略但课设里保留它显得更专业。getResourceAsStream读取的是src根目录下的配置文件所以db.properties必须放在源码包的src目录下而不是放在某个包里面。加载完成后用DriverManager.getConnection真正建立连接。这套DbUtil几乎是所有课设源码的通用模板你以后做图书管理、学生管理也能复用它。3.3 运行 SQL 脚本先建库再建表编码格式决定乱码源码包里的.sql文件不是拿来直接双击的要先打开确认表结构和库名再在 Navicat 或命令行执行。常见错误是直接右键运行结果在USE hotel_db处报错因为当前服务器没有这个库。正确做法是先创建一个名为hotel_db具体看脚本第一行的字符集utf8mb4的数据库再执行.sql文件。在 Navicat 中右键连接选择“运行 SQL 文件”注意选择一个选项“遇到错误时继续”和“导入前删除所有表”课设项目选删除旧表即可避免重复执行报错。脚本执行后查看一下t_room表里是否有初始房间数据。很多源码自带了一个INSERT INTO t_room ...的多行插入如果没有你就要手动添加 10 到 20 条房间数据否则打开界面没有房态可以看。添加数据的脚本可以在 Navicat 里用表格直接编辑也可以用命令INSERT INTO t_room (room_no, room_type, price, status) VALUES (101, 标准间, 168.00, 0), (102, 标准间, 168.00, 0), (201, 豪华间, 268.00, 0), (301, 总统套, 588.00, 0);批次插入时要注意status统一为0price不用加引号room_no必须唯一。如果之前测试建过表UNIQUE约束会让重复插入直接报错先执行TRUNCATE TABLE t_room;清空即可。跑完后在 Navicat 里双击t_room看到数据项目的第一步就算落地了。4. 核心功能实现拆解登录、开房、退房与商品消费4.1 登录窗口JFrame 加验证码的两种实现方式登录界面是课设的“门面”多数源码采用JFrameGridBagLayout或null布局。null布局在答辩时常被吐槽因为控件位置写死窗口拉大后按钮不会跟着变。但很多老源码为了省事就用setLayout(null)配合setBounds。你若要改进最省力的改动是换用GridBagLayout或者保留 null 布局但在componentResized事件里重算位置。这里不展开布局算法重点说登录逻辑。登录密码的处理是课设评分的一个细分点。用明文存储密码的源码太多你拿到后可以顺手升级成 SHA-256 存储。改动成本很低注册或初始化用户时存摘要登录时把输入框的值摘要后比对。这段代码可以直接写进UserDao.login方法中public boolean login(String username, String password) { String sql SELECT id FROM t_user WHERE username ? AND password ?; try (Connection conn DbUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { String encrypted encryptSHA256(password); ps.setString(1, username); ps.setString(2, encrypted); try (ResultSet rs ps.executeQuery()) { return rs.next(); } } catch (Exception e) { e.printStackTrace(); return false; } } private String encryptSHA256(String input) throws Exception { MessageDigest md MessageDigest.getInstance(SHA-256); byte[] bytes md.digest(input.getBytes(UTF-8)); StringBuilder sb new StringBuilder(); for (byte b : bytes) { sb.append(String.format(%02x, b)); } return sb.toString(); }这里用了 try-with-resources连接、preparedStatement 和 resultSet 都能自动关闭代码简洁且不会漏关。encryptSHA256把密码转成 64 位十六进制字符串你第一次把root密码摘要进去就能登录了。注意没有“加密”这一说这是摘要不可逆但课设答辩时讲清楚这一点反而是亮点。接口层仍返回boolean界面只弹“登录成功或失败”对话框这样业务逻辑与界面分离。登录验证码是防止暴力猜密码的简单手段。常见实现是在JPanel的paintComponent里画四个随机字符点击验证码图片区域时重新绘制。如果你在源码包的登录窗口里没找到验证码不必强行加但加上之后能直接提升“完整度”而且代码量不大。验证码生成的核心是BufferedImage和Graphics2D绘制噪点和干扰线然后把生成的字符串存到类的成员变量里提交时忽略大小写比较。需要注意生成验证码的随机数要用SecureRandom不要用Math.random()否则容易被人根据时间猜出序列。4.2 开房和退房事务边界在哪里开房功能是评审老师最爱追问的一段因为它涉及多张表更新。若源码里OrderDao.insert(order)和RoomDao.updateStatus(roomId, 1)各自独立提交中途客户插入成功但房间状态更新失败就会留下脏数据。改进方案是把这两步放在一个事务里你可以写一个新的OrderService类在其中获取同一个Connection关闭自动提交两条 SQL 都成功后再commit失败则rollback。这就是把 DAO 从单表操作上升到业务层的体现。下面给出一个开房事务的骨架注意conn.setAutoCommit(false)的位置public boolean checkIn(Order order, Customer customer) { Connection conn null; try { conn DbUtil.getConnection(); conn.setAutoCommit(false); // 第一步插入客户 int customerId customerDao.insert(conn, customer); // 第二步生成订单 order.setCustomerId(customerId); int orderId orderDao.insert(conn, order); // 第三步房间状态改为已入住 roomDao.updateStatus(conn, order.getRoomId(), RoomStatus.OCCUPIED); conn.commit(); return true; } catch (Exception e) { try { if (conn ! null) conn.rollback(); } catch (Exception ex) { ex.printStackTrace(); } e.printStackTrace(); return false; } finally { DbUtil.close(conn, null, null); } }关键点在于customerDao.insert(conn, customer)等方法签名里多了一个Connection参数这是为了共享同一个数据库连接。有些源码的 DAO 内部自己调DbUtil.getConnection()那就无法实现事务必须改。改的方法很简单在 DAO 方法里删掉Connection conn DbUtil.getConnection();改成方法参数传入。这个改动在答辩时可以明确说出“我把事务边界控制在 Service 层避免了部分成功”比堆功能更有说服力。退房事务与开房相反先计算费用更新订单状态再把房间状态置为空闲。费用计算常见两种按天数、按小时。课设一般按天数计算时要注意跨天问题用(checkOut.getTime() - checkIn.getTime()) / (1000*60*60*24)取整如果不足一天也要按一天计费。很多源码直接相除得到0导致客人住一晚免费这是高频 bug。正确写法是先除以一天的毫秒数若余数大于 0 再加一long diff checkout.getTime() - checkin.getTime(); int days (int) (diff / (24 * 60 * 60 * 1000)); if (diff % (24 * 60 * 60 * 1000) 0) { days; }这种向上取整的规则要在代码注释里写明因为需求上“凌晨入住算一天”是常见的酒店规则注释写清后答辩也能讲得顺。一定要处理checkout早于checkin的场景直接提示“退房时间不能早于入住时间”。4.3 预定与换房状态字段怎么设计才不翻车房间状态从“空闲/入住”两态扩展到“空闲/预订/入住/维修”四态后所有下拉框、按钮禁用逻辑都要同步调整。很多课设源码只做了两态预定功能是后加的经常出现“房间状态为预订但开房按钮仍然可用”的问题。这里的关键是写一个状态字典类或者至少用常量代替魔法数字。一个简单的状态枚举类public enum RoomStatus { FREE(0, 空闲), OCCUPIED(1, 已入住), RESERVED(2, 已预订), REPAIR(3, 维修中); private final int code; private final String desc; RoomStatus(int code, String desc) { this.code code; this.desc desc; } public int getCode() { return code; } public String getDesc() { return desc; } }界面上“开房”按钮根据RoomStatus来决定是否可点这样不会因为某个状态值拼写错误而翻车。换房逻辑的本质是“两个订单状态交换”新房间的状态改成已入住旧房间改成空闲并把订单的roomId更新到新房间。如果源码里没有换房功能建议不要加除非你的课程设计题目明确写了因为换房涉及订单历史追溯做不好反而暴露出状态管理混乱。若必须加记得在订单表中增加old_room_id字段或者在remark里记录。预定功能相对简单插入一条status2的订单并设置预定到期时间。到期未入住的定时任务在纯 Swing 项目里不好做课设通常不要求但你可以用Timer做一个 5 分钟内自动释放订房的模拟这在答辩时是很好的演示点。5. 课设项目避坑指南从数据库乱码到界面卡死5.1 现象界面中文全是问号插入数据后 Navicat 里也是乱码这是 Swing 课设里最普遍的翻车。原因有三层数据库创建时指定了latin1、连接串里缺少characterEncodingutf8、JTextField输入的内容在写入时经过操作系统默认 GBK 编码。大多数时候是第二层。解决把 MySQL 建库语句统一改成DEFAULT CHARSET utf8mb4连接串加上useUnicodetruecharacterEncodingutf8mb4并把 IDE 的项目文件编码设置为 UTF-8。注意 Navicat 的查询窗口也要改成 UTF-8。改完这三处乱码通常就消失了。如果还不消失检查 MySQL 服务端默认字符集在命令行执行show variables like character_set_%;。如果显示character_set_server是latin1修改 MySQL 安装目录下的my.ini在[mysqld]段加一行character-set-serverutf8mb4重启服务。这里要特别提醒千万不要在JTextField的getText()后手动new String(text.getBytes(GBK), UTF-8)这种写法那是误传改完后数据库里还是会乱码。5.2 现象点击按钮后界面卡死CPU 飙升原因是把 JDBC 操作直接放在了 Event Dispatch Thread事件分发线程中连接数据库耗时executeQuery如果遇到慢查询界面会一直转圈。解决把耗时操作放进SwingWorker或新线程。课设里最简单的替代方案是用一个新线程执行查询查询完成后通过SwingUtilities.invokeLater更新界面new Thread(() - { ListRoom roomList roomDao.findAll(); SwingUtilities.invokeLater(() - { roomTableModel.setRooms(roomList); roomTableModel.fireTableDataChanged(); }); }).start();注意不要直接在子线程里调用table.setModelSwing 组件不是线程安全的。fireTableDataChanged()触发表格重绘前提是roomTableModel实现了AbstractTableModel。很多源码用的是DefaultTableModel直接用setDataVector更新即可不需要触发事件但DefaultTableModel的列类型不灵活对日期排序不友好。如果只追求不卡死记住一点任何耗时超过 100 毫秒的操作都别放在按钮的actionPerformed方法体里。5.3 现象运行时报java.lang.ClassNotFoundException: com.mysql.jdbc.Driver这种报错几乎总是指向同一个原因驱动包没有进构建路径。在 Eclipse 中光把 jar 放到lib目录是不够的必须右键项目 → Build Path → Configure Build Path → Add JARs选到那个 jar。IDEA 则是在 Project Structure → Modules → Dependencies 中添加 jar。检查时不要只看项目里有没有这个 jar要看编译输出目录WEB-INF/lib或target/classes下有没有。如果你把项目从 Eclipse 导入 IDEA容易出现构建路径丢失最稳妥的办法是删除工程文件后重新用“New Project from Existing Sources”导入。如果Class.forName之后依然报找不到可能是 jar 包版本与 JDK 不兼容。老项目配 JDK 17 时com.mysql.jdbc.Driver这一行直接会被跳过或报错解决就是用 5.1.49 版本驱动并把连接串改成com.mysql.cj.jdbc.Driver的格式。提醒一句很多“全新”的 MySQL 8 驱动要求serverTimezoneUTC不写会在建连时抛异常所以在db.properties的 url 里加上serverTimezoneAsia/Shanghai能省掉一堆麻烦。5.4 现象窗口能打开但表格里没有任何数据排查步骤如下先用 Navicat 执行同样的 SQL 确认表里有数据再确认登录用户查询的是同一数据库很多源码配置了一个叫hotel的库你建的是hotel_db连接串也对不上最后确认是否调用了显示数据的方法。有些源码用JScrollPane包裹JTable但主窗口布局只有一个按钮点击后才填充数据而源码包里的说明文档没说清楚这一步。搜DataInit或initTable方法在主窗口构造函数里补一次调用即可。另外检查ResultSet循环是否漏掉了next()的while循环或者把while写成了if。多表连接查询里最容易错的是列名SELECT r.room_no, o.total_price FROM t_room r, t_order o中如果两张表都有status列rs.getInt(status)会因歧义报错。这时要用别名列名写成r.status。这些都是低分课设源码反复出现的硬伤你在阅读时可以直接修改也是课设报告里很好的“改进点”素材。5.5 现象:操作后数据库连接数暴涨报Too many connections原因几乎可以确定是 JDBC 连接未关闭。老式写法只在finally里关了resultSet和statement但connection留着没关或者查询方法中conn是局部变量被 GC 前一直占着连接池线程。最彻底的办法是全部换成前面写的DbUtil.close(conn, ps, rs)或使用 try-with-resources。如果项目里用了数据库连接池部分高分课设用了 C3P0连接数要合理配置最大连接数 20 对课设够用超过后getConnection会等待界面再次卡死。为什么源码跑几十分钟后突然报Too many connections除了连接泄漏还有可能是当前 MySQL 的max_connections默认值为 151而你的程序每次操作开两个连接一个给查询房间一个给订单表叠加反复开关窗口很快就耗尽。在开发阶段可先执行SET GLOBAL max_connections 500;应急但课设报告里不要写这个正确的结论是“规范关闭连接”。6. 别被课设框住把酒店管理系统做成能答辩的加分项目如果你已经跑通源码接下来要做的是在三日之内把它变成“你自己的项目”。最省力的加分动作有三个。第一把界面主色调统一成一个品牌色Swing 默认的灰色按钮换成UIManager.put(Button.background, new Color(...))再用FlatLaf主题替换默认外观只需加一个 jar、在main方法里FlatLightLaf.setup()整体观感立刻不同但这属于外观级改进不要花超过半天。第二增加一个“经营报表”面板用JFreeChart显示最近一周的入住率柱状图和营业额折线图这需要引入新的 jar但功能就是写两条统计 SQLSELECT room_type, COUNT(*) FROM t_order GROUP BY room_type图表数据填充是模板代码答辩时可视化效果非常加分。第三也是最重要的写一个“多条件查询”面板房号、类型、状态三个条件组合查询用if拼接 SQL 并加入参数这会考验你对PreparedStatement动态 SQL 的理解也正好回应评审老师最爱问的“你这个查询是怎么拼条件”的问题。如果你更重视数据正确性可以在订单表上补一个触发器退房时自动更新房间状态这是其他同学很少做的亮点。不过我的建议是不要把时间耗在加第三个新功能上而是把已有功能里的事务、连接关闭、状态字段枚举这些细节做好答辩时主动讲“我用了 SwingWorker 防止界面卡死”“我用 SHA-256 摘要保存密码”比多一个粗糙的功能更有效。另一个实用的验证方法是在开房后手动关掉 MySQL 服务再操作退房如果程序没抛异常而是提示数据库连接失败说明异常处理过关如果界面直接卡死那正好把这个问题改掉也可以写进课设报告的测试环节。我自己带过的课设小组里翻车最多的不是不会写代码而是连db.properties的密码都忘了改成自己本机的 root 密码导致答辩现场演示失败。所以我最后用第一人称说一个习惯每次拿到别人的源码我会先把数据库连接配置改成绝对路径可查的环境变量或者用一个DetectDatabase类在启动时检查连通性弹窗提示“检查数据库连接”而不是静默失败。这个习惯在很多次临时演示中救了我。希望帮到你也祝你答辩顺利。本文还有配套的精品资源点击获取