简介这是一份面向Java课程期末大作业的酒店管理系统完整项目包适合正在做课设、毕业设计或期末项目的在校生。系统围绕客房预订、入住登记、退房处理、账单结算等核心业务展开代码中覆盖类与对象、继承多态、异常处理、JDBC/SQL数据库交互、Swing/JavaFX界面及多线程同步等要点能帮助读者理解一个完整管理系统如何分层设计与实现各业务模块通过数据访问层与界面层解耦便于复用和二次开发。压缩包共29个文件、约9.7MB含11个Java源码文件、1份设计报告doc文档以及jpg/png界面截图源码构成可运行主体报告可用于撰写课程设计说明、梳理需求与模块划分截图便于对照界面和预期效果。已有1641人学习/下载适合需要借鉴项目结构、数据库表和基础设计模式如MVC、单例、工厂的Java学习者。1. Java 期末大作业的酒店管理系统它要的是什么又卡住了谁期末拿到「酒店管理系统源代码设计报告」这个题目时大多数人第一反应是去网上找一份能跑的源码改个包名就交。这个思路本身没错但真正卡住人的从来不是代码能不能跑而是答辩现场老师随口一句「你这个房间状态怎么流转的」「预订 and 入住有什么区别」你答不上来。这门课的及格线不在代码写得多高级而在三件事代码和报告能对上、现场能独立跑通、每一个出现在报告里的类和方法你都能讲明白。本文按我做这类课设的完整路径把技术选型、数据库设计、核心代码、常见翻车点和报告写作顺序写清楚适合正在做 Java 期末大作业、需要交付源码加设计报告的同学也适合想快速搭一个管理类系统模板的初级开发者。2. 技术选型为什么期末场景下 Swing JDBC 仍然是最稳的组合2.1 三条主流技术路线的取舍酒店管理系统作为期末大作业常见做法有三条路线Swing JDBC MySQL 的桌面客户端、JavaFX MyBatis 的桌面程序、Spring Boot Vue 的 Web 前后端分离。很多同学一上来就想做 Web觉得好看、有面子但忽略了一个事实期末大作业的验收重点是「代码结构是否清晰、设计报告是否完整、答辩能否讲清楚」而不是界面多炫。三条路线的实际差距集中在这张表里对比项Swing JDBC MySQLJavaFX MyBatisSpring Boot Vue学习成本低Swing 控件拖拽即可中需要理解 FXML 和属性绑定高前后端都要熟答辩风险低代码直观中涉及线程与生命周期高随便一个 CORS 或依赖问题就黑匣子工作量5 张表 3 个界面就能交界面更现代但工作量不减需要额外写接口文档环境要求JDK MySQL 一个 jarJDK JavaFX SDKJDK Maven Node报告匹配度高三层结构好画图中低容易写虚我一般会直接建议选第一条路线。原因很实际Swing 是 JDK 自带的不需要额外装运行时JDBC 是 Java 基础课必讲的内容老师问到底层你不会露怯MySQL 在课程设计里是标配。等你真把 Swing 这版做完了想升级成 Web 版业务逻辑和 DAO 层是可以平移的不算白做。2.2 为什么用 JDBC 而不是 MyBatis很多热词搜「mybatisplus根据java实体类生成创建表的sql语句」说明现在培训班和网课都在推 MyBatis Plus。但期末大作业里引入 MyBatis 会带来三个新问题第一你需要额外引入 mybatis.jar 和 mybatis-plus.jar而且版本要和 JDK 匹配第二MyBatis 的 mapper XML 和注解对新手是个黑匣子一个 namespace 写错就整站跑不起来第三设计报告里你得花篇幅解释 ORM 的原理答辩时反而多了一堆可被追问的点。JDBC 的问题在于代码啰嗦但它的好处是「每一条数据怎么来的、每一行代码在干什么」都是透明的。期末作业的场景下透明比简洁更重要。一个 Connection、三个 Statement 方法、两个 ResultSet 读取这些本身就是考点你写在报告里老师只会觉得你基础扎实。数据库连接参数集中放在 db.properties 里后续上线给 MySQL 换端口也只需要改一行。2.3 三层结构怎么落包期末大作业最常见的做法是三层结构view 层放 Swing 界面dao 层放数据库操作entity 层放实体类另外加一个 util 包放 DBUtil 这样的通用工具。包结构按这个习惯来src/ entity/ Admin.java Room.java Customer.java Reservation.java dao/ AdminDao.java RoomDao.java ReservationDao.java util/ DBUtil.java view/ LoginFrame.java MainFrame.java resources/ db.properties为什么这么分因为设计报告里通常要求你画「包图」和「类图」你分包分得越清晰图越好画。我见过不少同学把所有类堆在 default 包下面报告里那张包图画出来自己都嫌丢人。另外三层结构天然对应报告里的「表现层—业务逻辑层—数据访问层」描述写概要设计那一章时几乎可以直接复用。3. 数据库设计从 ER 设计到建表 SQL五张表结构与房间状态机3.1 实体关系与字段约定酒店管理系统的核心业务链是「管理员登录 → 客户登记 → 预订客房 → 入住 → 退房结账」。围绕这条链我一般会设计五张表管理员表 admin、客户表 customer、客房类型表 room_type、客房表 room、预订表 reservation。为什么不把客户和预订合并因为一个客户可能多次入住预订记录又有自己的入住和退房时间拆开以后写 SQL 会更顺报告里的 E-R 图也更好看。字段设计有几个约定要提前定好主键统一用自增 id时间字段用 datetime 而不是 varchar金额字段用 decimal(10,2) 而不是 double因为 double 在涉及金额累加时会有浮点误差虽然期末作业数据量小看不出来但老师说出去会显得你很业余。密码字段用 varchar(32) 存 MD5 的十六进制结果具体加密逻辑放后面代码章。房间状态我习惯用整数 0/1/2/3 表示「空闲/已预订/已入住/脏房」这样一条 update 就能完成状态流转比字符串状态写得快也比布尔字段扩展性强。3.2 建表 SQL基于 MySQL 8 的可复现脚本下面是这套系统的核心建表脚本MySQL 5.7 和 8.0 都能直接跑。注意设置了 utf8mb4 字符集否则后面存中文姓名和备注必乱码。CREATE DATABASE IF NOT EXISTS hotel_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hotel_db; CREATE TABLE admin ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password CHAR(32) NOT NULL COMMENT MD5之后的值 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE customer ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, phone VARCHAR(20) NOT NULL, id_card VARCHAR(18) DEFAULT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE room_type ( id INT AUTO_INCREMENT PRIMARY KEY, type_name VARCHAR(30) NOT NULL, price DECIMAL(10, 2) NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE room ( id INT AUTO_INCREMENT PRIMARY KEY, room_no VARCHAR(10) NOT NULL UNIQUE, type_id INT NOT NULL, status TINYINT NOT NULL DEFAULT 0 COMMENT 0空闲 1已预订 2已入住 3脏房, CONSTRAINT fk_room_type FOREIGN KEY (type_id) REFERENCES room_type(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE reservation ( id INT AUTO_INCREMENT PRIMARY KEY, customer_id INT NOT NULL, room_id INT NOT NULL, check_in_date DATE NOT NULL, check_out_date DATE NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, status TINYINT NOT NULL DEFAULT 0 COMMENT 0生效 1已完成 2已取消, CONSTRAINT fk_res_customer FOREIGN KEY (customer_id) REFERENCES customer(id), CONSTRAINT fk_res_room FOREIGN KEY (room_id) REFERENCES room(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这段脚本的关键点有三个一是 room 表用 type_id 外键关联 room_type这样标准间、大床房、行政房的价格只维护一份后期涨价只需 update 一张表二是 reservation 表同时关联 customer 和 roomE-R 图上能画出两个一对多关系报告内容更饱满三是所有表都加上了外键约束老师在检查数据库时看到「关系」是真实存在的而不是只在文档里画着好看。3.3 初始数据让系统第一次启动不是空的建完表之后要给几间房和一条管理员账号不然程序启动后查不到数据你还以为是代码坏了。管理员账号我一般手工插入一条密码为 MD5 值的数据MD5 的生成后面代码章的 Login 模块会处理这里先用一个已知值。INSERT INTO admin (username, password) VALUES (admin, 21232f297a57a5a743894a0e4a801fc3); INSERT INTO room_type (type_name, price) VALUES (标准间, 168.00), (大床房, 218.00), (行政套房, 388.00); INSERT INTO room (room_no, type_id, status) VALUES (101, 1, 0), (102, 1, 0), (103, 1, 0), (201, 2, 0), (202, 2, 0), (301, 3, 0), (302, 3, 0);这套初始数据的妙处在房间编号和房型 id 直接对应1 楼是标准间2 楼是大床房3 楼是行政房。演示的时候老师说「给我开一间 2 楼的房间」你在界面上按房号就能定位这种细节在答辩时非常加分。4. 核心源代码逐段拆JDBC 连接、DAO 查询与登录事件怎么串起来4.1 配置文件与数据库连接入口先建 db.properties 放在 resources 目录下所有连接的参数集中在这里。用文件而不是在代码里硬编码这是我踩过坑后的习惯有一年我直接写在 DBUtil 里换一台电脑演示MySQL 密码不一样不得不现场改代码重新编译场面极其尴尬。jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hotel_db?useSSLfalsecharacterEncodingutf8serverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password你的数据库密码这里的每一项都有讲究。driver 用com.mysql.cj.jdbc.Driver是 MySQL 8.x 对应的驱动类名如果你用的是 mysql-connector-java 5.1.x类名是com.mysql.jdbc.Driver新版还保留了这个类名做兼容但会打印 deprecation 告警别慌不是错误。URL 里useSSLfalse是必须的否则 MySQL 8 默认尝试 SSL 握手可能报 SSL 异常characterEncodingutf8保证中文不会变成问号serverTimezoneAsia/Shanghai解决驱动和 MySQL 时区不一致导致的时间报错allowPublicKeyRetrievaltrue是 MySQL 8 在非 SSL 连接下会遇到的坑不加会在首次连接时报「Public Key Retrieval is not allowed」这一段就是传说中的期末大作业玄学重灾区。接下来是最常用的 DBUtil。类只有一个静态方法 getConnection读取 properties 文件并返回连接关闭连接的 close 重载我一般写三个分别处理 Connection、Statement、ResultSet。package util; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; public class DBUtil { private static String url; private static String username; private static String password; static { try (InputStream in DBUtil.class.getClassLoader() .getResourceAsStream(db.properties)) { Properties props new Properties(); props.load(in); Class.forName(props.getProperty(jdbc.driver)); url props.getProperty(jdbc.url); username props.getProperty(jdbc.username); password props.getProperty(jdbc.password); } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, username, password); } public static void close(Connection conn, Statement stmt, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException ignored) {} try { if (stmt ! null) stmt.close(); } catch (SQLException ignored) {} try { if (conn ! null) conn.close(); } catch (SQLException ignored) {} } }逻辑说明静态代码块在类第一次被加载时执行读取配置文件并注册驱动实际连接在 getConnection 里通过 DriverManager 创建。这样整个项目里所有 DAO 不需要关心驱动注册只需要调用DBUtil.getConnection()。参数说明getResourceAsStream 的路径是相对于 classpath 的db.properties 必须放在源码目录的 resources 根下否则返回 null加载空文件后连接参数全是空运行时报 NullPointerException。4.2 RoomDao查询空房与预订时的状态流转酒店管理系统最核心的一张表是 room最核心的 DAO 方法就两个查可用房、在预订时扣房。这里直接查状态为 0 的空房并按房号排序。package dao; import entity.Room; import util.DBUtil; import java.sql.*; import java.util.ArrayList; import java.util.List; public class RoomDao { public ListRoom findAvailableRooms() { ListRoom list new ArrayList(); String sql SELECT r.id, r.room_no, t.type_name, t.price FROM room r JOIN room_type t ON r.type_id t.id WHERE r.status 0 ORDER BY r.room_no; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql); ResultSet rs ps.executeQuery()) { while (rs.next()) { Room room new Room(); room.setId(rs.getInt(id)); room.setRoomNo(rs.getString(room_no)); room.setTypeName(rs.getString(type_name)); room.setPrice(rs.getBigDecimal(price)); list.add(room); } } catch (SQLException e) { e.printStackTrace(); } return list; } }这段代码用了 try-with-resources连接、语句、结果集在 try 结束后自动关闭不用手动调 close。JOIN 查询比先查 room 再查 room_type 少一次数据库往返数据组装也顺手把房型名和价格带出来了界面显示时不用再查字典表。返回值是 List 而不是 ResultSet这是 DAO 层的基本要求ResultSet 依赖连接连接关了就没了转成实体类列表后界面层才能安全使用。预订时的状态更新一般放在一个事务里。先检查房间当前状态如果已被占就抛异常否则把状态从 0 改成 1同时往 reservation 表插入记录。熟练的话可以直接在一个 DAO 里完成但期末作业我建议拆开RoomDao.updateStatus 负责改状态ReservationDao.insert 负责插记录由业务层 Service 类控制事务边界。这样设计报告的类图能多画一个 Service 类时序图也更完整。4.3 登录逻辑MD5 校验与 Session 模拟登录是每个管理系统的门面。前端的输入框只是一个文本框核心在 AdminDao 里。密码不能明文比对因为数据库里存的是 MD5。MD5 虽然不安全但作为课程设计老师看到你做了散列存储已经算及格了。package dao; import java.security.MessageDigest; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; public class AdminDao { public static String md5(String input) { try { MessageDigest md MessageDigest.getInstance(MD5); byte[] digest md.digest(input.getBytes(UTF-8)); StringBuilder sb new StringBuilder(); for (byte b : digest) { sb.append(String.format(%02x, b)); } return sb.toString(); } catch (Exception e) { throw new RuntimeException(MD5加密失败, e); } } public boolean login(String username, String password) { String md5Pwd md5(password); String sql SELECT id FROM admin WHERE username ? AND password ?; try (Connection conn DBUtil.getConnection(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, md5Pwd); try (ResultSet rs ps.executeQuery()) { return rs.next(); } } catch (Exception e) { e.printStackTrace(); return false; } } }参数说明String.format(%02x, b)是把每个字节转成两位十六进制这样无论输入是什么出来的 MD5 一定是 32 位小写字符串和建表时手动插入的21232f297a57a5a743894a0e4a801fc3能对上。登录判断用 rs.next() 返回是否存在记录不要用 SELECT COUNT(*) 再取数少一层取值逻辑。密码输入框建议用 JPasswordField 而不是 JTextField后者会把密码明文显示在界面上用new String(passwordField.getPassword())取内容时拿到的是 char 数组转成 String 时注意判空。Swing 登录按钮的事件监听器写法如下这也是期末作业里最常见的「复用模板」代码btnLogin.addActionListener(e - { String username usernameField.getText().trim(); String password new String(passwordField.getPassword()); if (username.isEmpty() || password.isEmpty()) { JOptionPane.showMessageDialog(LoginFrame.this, 用户名和密码不能为空); return; } AdminDao dao new AdminDao(); if (dao.login(username, password)) { JOptionPane.showMessageDialog(LoginFrame.this, 登录成功); new MainFrame().setVisible(true); dispose(); } else { JOptionPane.showMessageDialog(LoginFrame.this, 用户名或密码错误); } });这里的dispose()是关键小细节登录成功后关闭当前登录窗口否则演示时两个窗口叠在屏幕上老师会觉得你连窗口生命周期都没理清。界面的布局直接用 Swing 默认的 FlowLayout 就能交差不需要引入任何第三方美化库重点是事件监听器里的业务逻辑要完整。5. 期末作业避坑五个让现场演示翻车的坑与处理记录5.1 驱动类找不到ClassNotFoundException 与连接超时现象运行程序点击登录按钮后控制台抛java.lang.ClassNotFoundException: com.mysql.cj.jdbc.Driver或者代码在 IDE 里编译通过但一执行就报错。原因代码能编译是因为 IDE 的 classpath 里有 jar但运行时没有。最常见的情况是把 mysql-connector-java 的 jar 放到了某个文件夹却没添加到项目构建路径或者换了机器之后jar 还在原路径新机器上根本找不到。解决确认 jar 确实在项目里。Eclipse 里右键项目 → Build Path → Add External ArchivesIDEA 里 File → Project Structure → Modules → Dependencies添加 jar 后勾选Compile和Runtime两个范围。注意检查 jar 是否被版本控制忽略有些同学的工程同步到网盘或 git 时把 jar 漏了换电脑 clone 下来才发现缺。5.2 MySQL 8 的 SSL 与公钥校验Public Key Retrieval is not allowed现象连接字符串没带额外参数首次执行查询时报Public Key Retrieval is not allowed或者报Communications link failure、SSL connection error。原因MySQL 8.x 默认使用 caching_sha2_password 认证插件在非 SSL 连接下首次认证需要获取服务器公钥。驱动为了安全不主动公钥检索就需要在 URL 里显式放行。解决在 db.properties 的 url 里补上allowPublicKeyRetrievaltrueuseSSLfalse这是接 MySQL 8 最省事的两行参数。如果是 5.7 的库加了也不会报错只是多余。5.3 中文乱码界面和数据库都是问号现象管理员在界面上输入「张三」点保存再查出来变成「???」或者界面上直接显示乱码。原因乱码有两条链一是数据库端建库时没指定 utf8mb4用了默认的 latin1二是 JDBC 连接端URL 没带 characterEncodingutf8驱动用客户端系统默认编码发送中文两边编码不一致。两个问题只要中了一个就会乱。解决先把已在用的库改成 utf8mb4按第 3 章的建库脚本重建最干净然后确认连接 URL 带着characterEncodingutf8。如果界面本身还是乱码检查源码文件编码是不是 UTF-8Windows 下 IDEA 默认是 UTF-8但老版本 Eclipse 可能是 GBK在 Run Configuration 的 VM options 里加-Dfile.encodingUTF-8能救回大部分场景。5.4 导出可运行 jar 后跑不起来配置文件没打进去现象IDE 里一切正常导出 Runnable JAR 后双击或命令行运行报Exception in thread main java.lang.NullPointerException定位到 DBUtil 的 props.load(in)。原因Eclipse/IDEA 导出 jar 时默认不会把 resources 目录打进去或者打了但路径不对getResourceAsStream 返回 null。解决导出时在打包选项中把 resources 目录勾进来更稳的做法是把 db.properties 放到 src 根目录而不是单独的 resources 目录这样导出时一定在 classpath 里。另外发布演示时MySQL 服务和数据库要在同一台机器上并处于启动状态我见过太多人演示到一半才发现 MySQL 服务没开然后当众等启动。5.5 设计报告和代码对不上字段名、类名、功能全不一致现象报告里的类图画的 AdminService 在代码里根本不存在数据库表字段明明是 create_time报告表格里写成了 reg_time报告说做了「会员折扣」点进程序根本没有这个功能。原因报告用别人的模板改了改代码也是东拼西凑两份材料互相独立。这在查重环节和答辩时是致命的——老师随便抽一个类问「这个是干嘛的」你翻代码找不到现场就会陷入沉默。解决写完代码之后再写报告以代码为准。报告里每个类、每个方法都要在代码里真实存在数据库表结构直接从数据库里导出来贴进报告而不是凭记忆写。自己先跑一遍页面确认哪些功能做完、哪些没有报告里只写做出来的部分宁少勿假。6. 设计报告与答辩怎么把报告写得像自己写的报告的结构我一般按软件工程的标准模板走但每一章都压缩到能对着代码讲出来的程度一、需求分析画一个用例图写 5 条功能性需求加 2 条非功能性需求别贪多二、概要设计放系统架构图加 E-R 图E-R 图直接用数据库里的表倒推三、详细设计类图和时序图类图里的每个类都要能在 src 里找到四、数据库设计直接贴第 3 章那五张表的建表 SQL加一张字段说明表五、测试写一张测试用例表记录「输入正确的密码能登录、输入错误密码会提示」这类真实跑过的手工测试六、总结写你遇到了哪个坑、怎么解决的。画图工具用 draw.io 或 ProcessOn 都行重要的是图和你代码一致。时序图画「预订房间」这一个场景就够客户点预订 → MainFrame 调 ReservationService → Service 调 RoomDao.updateStatus ReservationDao.insert画完你就知道答辩时该讲什么。答辩时老师高频问的问题就那么几个为什么选 Swing 不选 Web回答「期末项目周期短Swing 部署轻量不需要浏览器兼容」即可密码为什么是 MD5回答「防止数据库泄露后明文密码直接暴露」Room 的状态字段为什么用 int 不用 String回答「int 比较和状态流转写起来更简洁也方便后续扩展」。这些问题基本等于 java 八股文里最基础的那几条提前准备每个问题准备两句话不要在台上长篇大论。最后一个血泪经验演示前把数据库重启一遍、程序清掉缓存重新跑一遍不要用 IDE 里开着几小时的旧运行实例那不算你的程序能跑。我数不清多少次是「刚才还能跑答辩就挂了」——大多是旧连接、旧缓存、MySQL 没启动这三个里至少挂一个。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?