简介面向数据库课程设计的酒店管理系统客房管理项目文件适合高校计算机专业学生完成数据库综合实践时参考。压缩包共30个文件含10个Java源文件、18个class文件及2张jpg界面图整体仅404KB结构精简已有1876人学习下载。内容围绕客房管理核心流程覆盖客房、客户、订单、入住/退房等实体建模以及MySQL或SQL Server下的表结构设计、主外键约束和SQL增删改查操作。Java源码通过JDBC连接数据库并结合Swing实现预订、入住、查询等图形界面编译后的class文件可直接运行验证两张jpg直观展示系统交互效果。对于需要完成酒店管理系统课程设计、掌握关系型数据库实际应用的同学这份资源提供了从数据模型到编码实现的完整参考。1. 数据库课程设计选酒店管理系统客房管理才是真正的分水岭数据库课程设计做酒店管理系统很多同学一上来就铺开订房、餐饮、会员、停车结果答一开打连“当前可售房”都讲不清。这份资源没有走大而全的路子而是把模型压在客房管理一条主链路上房型、客房、客户、预订、入住、结算基础数据围绕“房态”互相咬合。它解决的是课设里最容易被答辩老师追问的场景——一张订单从预订到退房数据库层怎么保证状态不串、金额可算、报表能查。适合准备“酒店管理系统”数据库课设的同学也适合想抄一套带存储过程脚本的从业者。先给结论这是拿来改的底稿不是开箱即交的成品你愿意花半天时间把表的职责捋明白比直接抄全套表收益大得多。2. 先定业务边界客房状态、房价与订单怎么建模2.1 把住店流程拆成状态机房态是主心骨我拆这套资源的第一件事不是看 create table而是先把住店流程画成状态机。一次完整的客人动线是查询可售房 → 下预订单 → 到店办理入住 → 退房结账 → 房间转保洁。如果把动作直接映射成表就会得到很多张“操作流水表”数据越存越多状态越查越乱。更稳的做法是抽象出一张客房表用 room_status 字段表示房间当前状态预订和入住分别用过程表记录状态机只由后台动作来驱动。这里的 room_status 约定0 可售、1 已预订、2 入住中、3 保洁中、4 维修中。已预订和入住中必须分两态因为一个客人可能下个月才到店此时房间不该被其他预订占用也没到“入住中”。如果只用“占用 / 空闲”两个状态就撑不起预订场景。同理退房后的房间不能直接可售必须先进入保洁中由保洁动作改成可售这样管理人员能看到待清理房间。状态值不要写成中文表里只存 int解释留在代码注释和视图里。我把表规划成五个主要对象职责分得很清楚表名主键关键外键回答的问题room_typetype_id无这个房型多少钱、能住几个人roomroom_idtype_id具体哪间房、在哪层、当前什么状态customercustomer_id无哪位客人、手机号是什么reservationreservation_idcustomer_id、room_id客人打算什么时候住哪间房staystay_idreservation_id、customer_id、room_id实际谁住进来了、住了几天把“预期”和“事实”分开是这份资源能讲清楚的关键。reservation 里存的是客人下单时填的到店离店日期stay 里存的是实际 check-in 和实际 check-out。两者重合但不合并以后做改期、换房、赔偿计算都有原始依据。2.2 数据库选型与字符集课设里最不容易出错的组合课程设计没有生产环境的容量压力选型的核心是稳定、能演示、能导出报告。我一般给学员固定用 MySQL存储引擎选 InnoDB理由不外乎三点能开外键能开事务能写存储过程、视图和触发器装好后在客户端工具里导出 SQL 方便答变时遇到报错也容易重新初始化。不要把精力耗在“到底用哪个数据库好”上选一个身边同学用得最多的遇到问题才好借参考环境。字符集第一次就要写死。这里如果漏掉 DEFAULT CHARACTER SET后续导入数据时很可能遇到中文乱码尤其用文本编辑器保存脚本时默认编码经常不是 UTF-8导入后直接变问号。解决方式是建库指定 utf8mb4脚本另存为 UTF-8命令行连接再带字符集参数。别小看这一步课设数据库乱码是答辩现场翻车率最高的问题之一。CREATE DATABASE IF NOT EXISTS hotel_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;IF NOT EXISTS 保证脚本重跑不会报错utf8mb4 覆盖绝大多数常用字符不只是中文包括表情符号和特殊符号utf8mb4_unicode_ci 作为排序规则中文排序在大多数课设环境里表现稳定。后期想改库的字符集也能靠这条语句补救但已经建好的表不会自动跟着变所以最好在建库时就锁死配置。2.3 主外键与删除策略RESTRICT、CASCADE 不是随便选ER 图一旦定下来第二要命的是外键级联策略。我用一句口诀业务主数据防误删过程数据保历史。customer、room、room_type 这类主数据子表有引用时不允许删外键用 RESTRICTreservation 取消后对应记录删不删我的看法是不删只把状态改成已取消这样答便查得到“同一间房曾经被哪些订单占用过”。stay 入住记录更是要留底账务、审计、入住率统计都要读它。有的课设为了演示“删除客户”功能把 customer 的删除设成 CASCADE一删客户入住和账单全没了数据完整性直接被打成筛子。所以这份资源里的外键基本不设 CASCADE。唯一允许 CASCADE 的场景是订单明细这类从属表父单删掉、明细一起删才合理。设计阶段把每个外键策略写下来后面建表就是照抄。客户和预订之间我更倾向于不做物理删除而是给 customer 表加一个 is_deleted 逻辑删字段让演示人在页面上“删除”后历史记录还能被 SQL 查到。MySQL 的 InnoDB 里如果外键列是 NOT NULL 且约束为 RESTRICT删除被引用行直接报错只有当外键列允许 NULL 且指定 ON DELETE SET NULL才有删除后置空的效果。我把 stay.reservation_id 设为可空就是想让散客入住这条路径走得通又不把预订历史强行绑死。3. 落地建表脚本把 ER 图变成能跑的 SQL3.1 基础表结构房型、客房、客户业务模型定完之后就可以动手建表。下面是资源里最核心的基础表结构我精简了字段保留了课程设计必用的部分。先建房型再建客房因为客房表的 type_id 要引用房型表客户表独立不依赖其他表。CREATE TABLE room_type ( type_id INT AUTO_INCREMENT PRIMARY KEY, type_name VARCHAR(30) NOT NULL, base_price DECIMAL(8,2) NOT NULL, bed_count TINYINT NOT NULL DEFAULT 1, max_guests TINYINT NOT NULL DEFAULT 2, is_active TINYINT NOT NULL DEFAULT 1 ) ENGINEInnoDB; CREATE TABLE room ( room_id INT AUTO_INCREMENT PRIMARY KEY, room_no VARCHAR(10) NOT NULL UNIQUE, floor_no TINYINT NOT NULL, type_id INT NOT NULL, room_status TINYINT NOT NULL DEFAULT 0, remark VARCHAR(100), CONSTRAINT fk_room_type FOREIGN KEY (type_id) REFERENCES room_type(type_id) ) ENGINEInnoDB; CREATE TABLE customer ( customer_id INT AUTO_INCREMENT PRIMARY KEY, customer_name VARCHAR(30) NOT NULL, phone VARCHAR(20) NOT NULL, id_card VARCHAR(18) NULL, register_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, is_deleted TINYINT NOT NULL DEFAULT 0, UNIQUE KEY uk_customer_phone (phone) ) ENGINEInnoDB;DECIMAL(8,2) 最大支持 999999.99 的金额课设房价完全够用金额不用 FLOAT 或 DOUBLE因为二进制浮点算钱容易产生 0.1 级别的尾差答变时被追问“为什么 0.10.2 不等于 0.3”很难收场。room_no 加 UNIQUE保证同一家酒店里不会出现两个 101 房间。floor_no 用 TINYINT楼层不可能超过 127够用且省空间。is_active 和 is_deleted 都是逻辑删除字段后面所有业务查询默认只读 is_active 1 的数据。这里提一个命名坑表名不要直接用 order因为 order 在 SQL 里是排序关键字与其到处转义不如从源头命名成 reservation 或 booking 这类业务词。资源里的核心表都用了完整表名字段也避开 desc、condition 少数保留字脚本可移植性更好。3.2 过程表预订、入住、账单怎么挂外键基础表建完接下来是过程表。reservation 记录“计划”stay 记录“实际”bill 记录“钱”。三张表通过外键串起来又不能串得太死。CREATE TABLE reservation ( 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, status TINYINT NOT NULL DEFAULT 0, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, remark VARCHAR(200), CONSTRAINT fk_resv_customer FOREIGN KEY (customer_id) REFERENCES customer(customer_id), CONSTRAINT fk_resv_room FOREIGN KEY (room_id) REFERENCES room(room_id), CONSTRAINT chk_resv_dates CHECK (check_out_date check_in_date) ) ENGINEInnoDB; CREATE TABLE stay ( stay_id INT AUTO_INCREMENT PRIMARY KEY, reservation_id INT NULL, customer_id INT NOT NULL, room_id INT NOT NULL, actual_check_in DATETIME NOT NULL, actual_check_out DATETIME NULL, bill_amount DECIMAL(10,2) NULL, status TINYINT NOT NULL DEFAULT 0, CONSTRAINT fk_stay_resv FOREIGN KEY (reservation_id) REFERENCES reservation(reservation_id), CONSTRAINT fk_stay_customer FOREIGN KEY (customer_id) REFERENCES customer(customer_id), CONSTRAINT fk_stay_room FOREIGN KEY (room_id) REFERENCES room(room_id) ) ENGINEInnoDB; CREATE TABLE bill ( bill_id INT AUTO_INCREMENT PRIMARY KEY, stay_id INT NOT NULL, amount DECIMAL(10,2) NOT NULL, pay_status TINYINT NOT NULL DEFAULT 0, pay_time DATETIME NULL, operator VARCHAR(30), CONSTRAINT fk_bill_stay FOREIGN KEY (stay_id) REFERENCES stay(stay_id) ) ENGINEInnoDB;reservation 里为什么不放实际入住时间因为“计划”和“事实”必须分开。改期、晚到、提前退房都会影响实际记录如果混在一张表里你要么不断更新原订单字段要么丢失原始预约时间后续统计“预订转化率”就没依据。stay.reservation_id 允许 NULL是给散客现场开房留的口子没有预订也能直接入住仅当有预订存在时才关联过去。CHECK 约束需要 MySQL 8.0 以上的版本才会真正生效老版本只检查语法不检查数据课设文档里要写明环境要求。bill 表单独拆出来而不是把金额字段永久堆在 stay 上是为了支持一笔入住对应多笔支付、部分退款、预授权等场景。课程设计即使不做这么细有一张独立的账单表答办时也能说出“我在第三范式上做了拆分”比把金额塞进 stay 里好讲。3.3 索引、初始化数据和课程演示用的几间房表结构定了索引也要跟上。这里不是造复杂度而是让“查空房”和“查冲突”两条核心查询不扫全表。CREATE INDEX idx_resv_room_date ON reservation(room_id, check_in_date, check_out_date); CREATE INDEX idx_stay_room_status ON stay(room_id, status); CREATE INDEX idx_room_status ON room(room_status, floor_no);idx_resv_room_date 对应预订冲突检测查询条件通常同时带 room_id 和日期区间复合索引能直接命中idx_stay_room_status 服务“某房间当前是否有人入住”where 里先过滤 room_id再过滤 statusidx_room_status 服务“可售房列表”用 room_status 过滤后按楼层排序。初始化数据不用太多够演示就行。我习惯放三个房型、五间房、两个测试客户INSERT INTO room_type (type_name, base_price, bed_count, max_guests) VALUES (单人房, 198.00, 1, 1), (标准间, 298.00, 2, 2), (家庭房, 458.00, 2, 3); INSERT INTO room (room_no, floor_no, type_id, room_status) VALUES (101, 1, 1, 0), (102, 1, 1, 0), (201, 2, 2, 0), (202, 2, 2, 0), (301, 3, 3, 0); INSERT INTO customer (customer_name, phone, id_card) VALUES (演示客户A, 13800000001, 123456199001010011), (演示客户B, 13800000002, NULL);测试客户的名字起成“演示客户A / 演示客户B”一眼就能看出是测试数据答办时不会混淆真实业务数据。身份证可空但建议一个填一个不填这样既能展示完整信息又能证明表结构允许缺失敏感字段。房态初始化全部置 0后面的演示才方便从“新增预订”开始一步一步让状态动起来。3.4 整库脚本的导入顺序与重跑策略建表有依赖顺序重跑脚本时最怕“外键找不到父表”。我习惯把初始化分成 schema 和 data 两个文件用命令行导入mysql --default-character-setutf8mb4 -u root -p hotel_schema.sql mysql --default-character-setutf8mb4 -u root -p hotel_data.sql如果整个环境要重置先在外键检查关闭的状态下删表再从外键被依赖的表开始删SET FOREIGN_KEY_CHECKS 0; DROP TABLE IF EXISTS bill, stay, reservation, customer, room, room_type; SET FOREIGN_KEY_CHECKS 1;DROP 的顺序是子表在前、父表在后bill 依赖 staystay 依赖 reservation所以这条删除顺序不能乱。关闭外键检查只是为了清理环境业务运行时千万不要常开否则删父表时子表数据会变成孤儿记录答办时容易被导师顺着外键追出问题。如果你是第一次跑这套脚本看到Foreign key constraint fails先检查是不是父表还没建再检查字段类型是否完全一致类型不一致也会报外键错。4. 把业务规则写进数据库存储过程与状态视图怎么搭4.1 预订与入住的状态流转一个事务里完成校验和写库裸表增删改查只是课设中的第一层答辩老师更多盯着“连续动作”。把业务规则放进存储过程是这份资源的重点。前面已经建好 reservation 和 room下面这个 sp_create_reservation 把“库存校验、日期重叠校验、落表、改房态”放在一个逻辑里整个过程对调用方来说就是一次调用。DELIMITER // CREATE PROCEDURE sp_create_reservation( IN p_customer_id INT, IN p_room_id INT, IN p_check_in DATE, IN p_check_out DATE, OUT p_reservation_id INT ) BEGIN DECLARE v_status TINYINT; DECLARE v_conflict INT DEFAULT 0; SELECT room_status INTO v_status FROM room WHERE room_id p_room_id FOR UPDATE; IF v_status 0 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT room_not_available; END IF; SELECT COUNT(*) INTO v_conflict FROM reservation WHERE room_id p_room_id AND status IN (0, 1) AND p_check_in check_out_date AND p_check_out check_in_date; IF v_conflict 0 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT date_conflict; END IF; INSERT INTO reservation(customer_id, room_id, check_in_date, check_out_date, status) VALUES (p_customer_id, p_room_id, p_check_in, p_check_out, 1); SET p_reservation_id LAST_INSERT_ID(); UPDATE room SET room_status 1 WHERE room_id p_room_id; END// DELIMITER ;代码里有几个关键点要说明。SELECT room_status INTO v_status 后面跟了 FOR UPDATE这是对 room 行加排他锁第二个并发请求会等第一个事务提交后才能读避免两个请求同时读到“可售”。日期重叠条件写的是p_check_in check_out_date AND p_check_out check_in_date这个判断覆盖了完全重叠、部分重叠、包含等大多数情况相邻边界比如 1 号离开、2 号入住不会误判重叠这是最容易写错的地方。后面的 status IN (0, 1) 表示只统计有效预订已取消订单不阻塞新订单。SIGNAL 语句用来抛业务异常调用方拿到这个异常就知道是“房间不可售”还是“日期冲突”。实际课设里我会把整个调用放进一个事务里执行比如START TRANSACTION; CALL sp_create_reservation(1, 1, 2026-06-01, 2026-06-03, rid); COMMIT;如果中间出错事务会停在出错点需要手动做 ROLLBACK如果不回滚插入订单和修改房态会变成“一半成功”这是数据库一致性最典型的反面教材。答办时能主动讲出“我会把整个过程包在事务里”已经比大多数课设高半档。4.2 入住与退房结算把房间状态和账单金额一起更新预订只是开始真正的逻辑在入住和退房。入住时要把 reservation 状态从未入住改成入住中在 stay 里创建实际记录再把 room 改成入住中退房时反过来把 stay 记录补上离店时间算完金额把 room 改成保洁中。DELIMITER // CREATE PROCEDURE sp_check_in( IN p_reservation_id INT, IN p_room_id INT, OUT p_stay_id INT ) BEGIN DECLARE v_customer_id INT; DECLARE v_status TINYINT; SELECT customer_id, status INTO v_customer_id, v_status FROM reservation WHERE reservation_id p_reservation_id FOR UPDATE; IF v_status 1 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT reservation_not_confirmed; END IF; INSERT INTO stay(reservation_id, customer_id, room_id, actual_check_in, status) VALUES (p_reservation_id, v_customer_id, p_room_id, NOW(), 1); SET p_stay_id LAST_INSERT_ID(); UPDATE reservation SET status 3 WHERE reservation_id p_reservation_id; UPDATE room SET room_status 2 WHERE room_id p_room_id; END// CREATE PROCEDURE sp_check_out( IN p_stay_id INT ) BEGIN DECLARE v_room_id INT; DECLARE v_base_price DECIMAL(8,2); DECLARE v_days INT; DECLARE v_amount DECIMAL(10,2); DECLARE v_actual_in DATETIME; DECLARE v_actual_out DATETIME; DECLARE v_stay_status TINYINT; SELECT room_id, actual_check_in, actual_check_out, status INTO v_room_id, v_actual_in, v_actual_out, v_stay_status FROM stay WHERE stay_id p_stay_id FOR UPDATE; IF v_stay_status 1 THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT stay_not_active; END IF; SELECT rt.base_price INTO v_base_price FROM room r JOIN room_type rt ON r.type_id rt.type_id WHERE r.room_id v_room_id; SET v_actual_out COALESCE(v_actual_out, NOW()); SET v_days DATEDIFF(v_actual_out, DATE(v_actual_in)); IF v_days 1 THEN SET v_days 1; END IF; SET v_amount v_base_price * v_days; UPDATE stay SET actual_check_out v_actual_out, bill_amount v_amount, status 2 WHERE stay_id p_stay_id; UPDATE room SET room_status 3 WHERE room_id v_room_id; END// DELIMITER ;入住过程一次更新三张表任何一张失败都会让状态不一致房态变成已入住但 stay 没有记录。这就是为什么存储过程要做完整性校验而不是在应用层分三次 SQL 执行。退房结算里DATEDIFF 按自然日计算不精确到小时这是课程设计常见的计费简化必须在文档里注明IF v_days 1 THEN SET v_days 1防止客人住半天被算成 0 元。房间退房后统一改成 3 保洁中而不是直接回到 0 可售因为清洁动作还没有发生。做完这一步房间才允许被下一个客人预订。这一层还有一个容易忽略的细节sp_check_out 的 SELECT INTO 如果查不到记录MySQL 会直接报 No data 错误。正规做法是加一个条件处理器或者先做存在性检查课设里可以在应用层调用前先确认 stay_id 有效或者干脆在存储过程里加一个异常分支。这个坑留给后面的避坑笔记讲先知道有这回事。4.3 视图与报表把状态值翻译成答辩能看懂的字段房间状态在表里是 0、1、2、3演示时没人想看数字视图把数字翻译成中文也让报表查询变得非常短。CREATE OR REPLACE VIEW v_room_status AS SELECT r.room_id, r.room_no, r.floor_no, rt.type_name, rt.base_price, CASE r.room_status WHEN 0 THEN 可售 WHEN 1 THEN 已预订 WHEN 2 THEN 入住中 WHEN 3 THEN 保洁中 ELSE 维修中 END AS status_text FROM room r JOIN room_type rt ON r.type_id rt.type_id; CREATE OR REPLACE VIEW v_day_occupancy AS SELECT CURDATE() AS stat_date, COUNT(*) AS occupied_rooms, (SELECT COUNT(*) FROM room WHERE room_status 0) AS available_rooms FROM stay WHERE status 1 AND DATE(actual_check_in) CURDATE() AND (actual_check_out IS NULL OR DATE(actual_check_out) CURDATE());v_room_status 把所有房间状态一次性列出来前台查空房时直接SELECT * FROM v_room_status WHERE status_text 可售答办时也能展示“我把状态字典翻译放在了数据库层应用层不用知道数字含义”。v_day_occupancy 计算当前有多少在住房间、多少可售房入住率用 occupied_rooms 除以总房数就能得到。这里统计的是“当前时刻”不是历史累计所以叫日快照视图。视图的好处是让查询逻辑复用也让答办演示有一个明确的入口。导师问“有哪些房间”、“今天入住率多少”不必现场拼 SQL先SELECT * FROM v_room_status;再SELECT * FROM v_day_occupancy;整个数据库设计的完整性就立住了。5. 避坑笔记客房管理数据库最常见的五个数据库报错5.1 删除房型时被外键拦住不是数据库出 bug是设计故意现象同学想删掉一个房型执行 DELETE 后直接报Cannot delete or update a parent row: a foreign key constraint fails第一反应是“外键把这个功能堵住了”。原因room 表的 type_id 引用了 room_typeInnoDB 默认外键约束生效RESTRICT 策略不允许删除还被引用的父行。解决把“删”改成“停用”。执行UPDATE room_type SET is_active 0 WHERE type_id ...;查询时统一加条件WHERE is_active 1。如果坚持物理删除要先确认没有任何房间挂在房型下面再执行 DELETE。不要用SET FOREIGN_KEY_CHECKS0硬删答变问到“一致性怎么保证”时这就是送分题变成送命题。5.2 两笔订单重叠在同一间房并发预订的经典事故现象A 同学在自己的客户端下单成功B 同学同时在另一台电脑下单同一房间同一晚两边都显示成功后台查表有两条重叠订单。原因应用层先 SELECT 判断再 INSERT中间空隙另一个事务也读到“可售”没有行锁或唯一约束锁住区间两个请求同时通过校验。解决把预订操作收进存储过程SELECT 房间状态时执行 FOR UPDATE如果不想用存储过程也要把“查状态 插入订单 改房态”放进同一个事务并在事务开始处对 room 行加锁。第 4.1 的 sp_create_reservation 就是这个思路。注意事务一定要提交或回滚锁才会释放有的同学调试完没有 COMMIT页面看起来卡死就是锁没释放。5.3 SQL 脚本导入后中文乱码现象建库后插入中文SELECT 出来全是问号或者客户名变成“????”。原因三个环节至少一个不一致库字符集、客户端连接字符集、脚本文件的实际编码。常见文本编辑器保存 SQL 时默认不是 UTF-8MySQL 按库字符集读取时自然乱码。解决建库统一 utf8mb4脚本另存为 UTF-8 格式命令行导入加--default-character-setutf8mb4客户端工具导入时也要选 utf8mb4。还有一个小坑如果脚本里已经有 CREATE DATABASE 语句字符集设置要放在建表之前否则建表继承服务器默认值后期改库 charset 不会自动改已有表。5.4 状态字段用字符串统计报表一锅粥现象room_status 有时写“空闲”有时写“空闲中”有时写“可售”查询 GROUP BY 出现三种看似相同却分不到一组的分组。原因数据库没有约束取值应用层和手工维护随意写入状态值缺少单一事实来源。解决表里只用 TINYINT状态字典作为注释或单独配置表查询展示用 CASE 表达式翻译。如果你为了省事在表里存中文答辩老师一句“这个字段破坏了三范式字典表呢”就很难圆。可以单独建一张 state_dict 表或者至少把 0/1/2/3 的语义写进项目文档答变时能解释“数值是编码翻译在视图层”。5.5 删除客户把历史订单带没现象演示删除用户后通过手机号查不到历史订单连入住数据也一起消失。原因外键设置为 ON DELETE CASCADE子表数据被物理连带删除。客户主数据一删reservation、stay、bill 会跟着一条条消失。解决业务表不物理删除。给 customer 加 is_deleted 字段删除操作改成UPDATE customer SET is_deleted 1。查询默认过滤已删除后台需要修复数据时再放开条件查。stay 和 bill 涉及账务绝对不能级联删除客户可以有“拉黑”“注销”状态但不能从库里消失。数据留底才有后续审计和统计的可能。6. 答辩前最后一步用一组验真脚本把系统串起来课设做完不是把表建出来就结束而是要能证明“这套数据库能处理完整业务”。我一般会准备一份验收用的小脚本把预订、入住、退房、统计串成一条链路答辩现场从头跑一遍。-- 清空上次残留的用户变量 SET rid 0; SET sid 0; -- 1. 创建一笔预订客户A 预订 101 房6月1日入住6月3日离店 START TRANSACTION; CALL sp_create_reservation(1, 1, 2026-06-01, 2026-06-03, rid); SELECT rid AS new_reservation_id; COMMIT; -- 2. 故意用重叠日期再订同一间房预期会报 date_conflict CALL sp_create_reservation(2, 1, 2026-06-02, 2026-06-04, rid2); -- 3. 到店入住 CALL sp_check_in(rid, 1, sid); -- 4. 退房结算 CALL sp_check_out(sid); -- 5. 报表验证 SELECT * FROM v_room_status ORDER BY room_id; SELECT * FROM v_day_occupancy;这段脚本里每一步都有验证点。第一步跑通后 rid 会有值第二步同一房间为了重叠日期触发 SIGNAL说明数据库层的冲突校验不是摆设。第三步入住后查 v_room_status101 房的房态会从“已预订”变成“入住中”第四步退房后变成“保洁中”金额字段已经写进 stay。第五步查两个视图报表数据和前面手动操作的结果能对上整个闭环就成立了。如果答辩老师问“数据库比文件存储强在哪”可以现场加一段事务回滚演示START TRANSACTION; CALL sp_create_reservation(2, 2, 2026-06-02, 2026-06-04, r2); ROLLBACK; SELECT room_id, room_status FROM room WHERE room_id 2;回滚后房间状态应该还是 0说明“插入订单 修改房态”这一组操作被整体撤回了不会留下半截状态。这份资源的最后一层价值就在这里你要讲的不是背下来的 SQL 关键字而是数据库怎么保证一致性。我这些年带课设养成的习惯很固定提交前强制走一遍“建库 → 初始化 → 预订 → 入住 → 退房 → 统计”的验收链路再用重叠日期故意触发一次冲突确认系统是拒绝而不是默默接受。把这条链路跑通课设的完整度和答辩底气基本就稳了。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?