1. 实验目的

(1)可以帮助学生理解面向对象设计思想,复习MySQL语言程序设计技术,提升利用专业知识解决实际问题的能力。加深对数据结构这门计算机课程的理解和熟练一些数据结构经典算法的使用。实践这学期学习的数据库原理,初步了解软件开发的MVC设计模式,设计报告文档规范,能撰写比较规范的课程设计报告。

(2)《数据库》课程设计的基本目标是:培养学生对数据库知识的全面综合训练,把书上学到的知识用于解决实际问题,培养今后软件开发工作所需的动手实践能力,包括设计表格以及实现对表格的操作时的基本技能和技巧,以及一整套软件工作规范的训练和团体协作精神的培养。

(3) 通过此次课程设计的训练,能起到扩充知识,锻炼操作技能,培养分析问题、解决问题能力的作用。

(4)加强自己对实际问题的分析能力,以及如何更好的将一些经典的算法应用于实际。

(5) 数据库课程设计是对我们在大学所学知识的又一次综合考察,培养我们独立思考、分析和解决问题的能力。把我们所学的理论知识应用到实际中,加强理论和实践知识的联系,并为我走入工作岗位打下很好的基础。

2. 系统简介

酒店预订管理系统是一套功能全面、操作便捷的现代化酒店信息管理软件,可广泛应用于各类酒店、宾馆和民宿的日常运营管理。本系统以提升客户体验和管理效率为核心目标,致力于解决传统酒店管理中存在的房间信息不透明、预订流程繁琐、入住退房效率低下等问题。本系统以客户为中心,根据实际情况确定实现了以下功能::

(1)客户预订客房

(2)管理酒店客房基本信息(规格、价格、特点、剩余数、酒店类型等)

(3)管理酒店类型信息(类型名称、星级等)

(4)管理客户信息(姓名、性别、联系方式、入住时间、入住天数等)

(5)支持一个客户预订多个房间

(6)实现增删改查功能(精确查询、模糊查询、统计查询)

(7)事务处理验证

3. 系统需求分析

  1. 数据字典:

表1 HotelTypes

列名

描述

TypeID

酒店类型ID

TypeName

类型名称

StarLevel

星级

Description

描述

表2 Hotels

列名

描述

HotelID

酒店ID

HotelName

酒店名称

Address

地址

TypeID

酒店类型ID

Contact

联系方式

表3 Rooms

列名

描述

RoomID

房间ID

HotelID

所属酒店ID

RoomType

房间类型

Price

价格

Features

特点

TotalRooms

总房间数

AvailableRooms

可用房间数

表4 Customers

列名

描述

CustomerID

客户ID

Name

客户姓名

Gender

性别

Contact

联系方式

IDNumber

身份证号

表5 Reservations

列名

描述

ReservationID

预订ID

CustomerID

客户ID

RoomID

房间ID

CheckInDate

入住日期

CheckOutDate

退房日期

Days

入住天数

Status

状态(预订中/已入住/已取消)

TotalPrice

总价格

b、数据流图:

图1 简化的酒店管理系统数据流图

c、主要操作流程图:

图2 流程图

4. 概念模型设计

E-R图

实体:包含酒店类型(HotelTypes)、酒店(Hotels)、房间(Rooms)、客户(Customers)、预订(Reservations)五大实体,每个实体的属性已在图中标注。

关系:

酒店类型与酒店(1:N),一个类型可对应多家酒店。

酒店与房间(1:N),一家酒店包含多个房间。

客户与预订(1:N),一个客户可有多条预订记录。

房间与预订(1:N),一个房间在不同时间段可被多次预订。

设计意义:通过规范化设计消除冗余数据,确保数据完整性。

图3 E-R图

5. 逻辑模型设计

a、关系模式描述

1.酒店类型:HotelTypes(TypeID, TypeName, StarLevel, Description)

主键: TypeID

外键:无

2.酒店:Hotels(HotelID, HotelName, Address, TypeID, Contact)

主键: HotelID

外键: TypeID 引用 HotelTypes(TypeID)

3.房间:Rooms(RoomID, HotelID, RoomType, Price, Features, TotalRooms, AvailableRooms)

主键: RoomID

外键: HotelID 引用 Hotels(HotelID)

4.客户:Customers(CustomerID, Name, Gender, Contact, IDNumber)

主键: CustomerID

5.预订:Reservations(ReservationID, CustomerID, RoomID, CheckInDate, CheckOutDate, Days, Status, TotalPrice)

主键: ReservationID

外键: CustomerID 引用 Customers(CustomerID)

外键: RoomID 引用 Rooms(RoomID)

b函数依赖分析

1.HotelTypes 表:

{TypeID} → {TypeName, StarLevel, Description}

已在3NF

2.Hotels 表:

{HotelID} → {HotelName, Address, TypeID, Contact}

已在3NF

3.Rooms 表:

{RoomID} → {HotelID, RoomType, Price, Features, TotalRooms, AvailableRooms}

已在3NF

4.Customers 表:

{CustomerID} → {Name, Gender, Contact, IDNumber}

已在3NF

5.Reservations 表:

{ReservationID} → {CustomerID, RoomID, CheckInDate, CheckOutDate, Days,Status, TotalPrice}

已在3NF

c规范化处理

(1)HotelTypes 表

已经在第三范式(3NF),因为:

每个非主属性(TypeName, StarLevel, Description)完全依赖于主键TypeID

不存在部分依赖(所有属性都直接依赖于整个主键)

不存在传递依赖(没有非主属性依赖于其他非主属性)

(2)Hotels 表

初始状态在第一范式(1NF),所有属性都是原子值

已经在第二范式(2NF)

主键是HotelID

所有非主属性(HotelName, Address, TypeID, Contact)完全依赖于整个主键,没有部分依赖

已经在第三范式(3NF)

没有传递依赖

TypeID虽然是外键,但不构成传递依赖关系

所有非主属性都直接依赖于主键HotelID

(3)Rooms 表

初始状态在第一范式(1NF),所有属性都是原子值

已经在第二范式(2NF)

主键是RoomID

所有非主属性(HotelID, RoomType, Price, Features, TotalRooms, AvailableRooms)完全依赖于整个主键

已经在第三范式(3NF)

没有传递依赖

HotelID是外键,但不导致传递依赖

所有非主属性都直接依赖于主键RoomID

(4)Customers 表

初始状态在第一范式(1NF),所有属性都是原子值

已经在第二范式(2NF)

主键是CustomerID

所有非主属性(Name, Gender, Contact, IDNumber)完全依赖于整个主键

为了达到第三范式(3NF)的分析

检查传递依赖:IDNumber → Name, Gender, Contact

理论上可以进一步分解为:

Customer_Details(CustomerID, Name, Gender, Contact)

Customer_Identification(CustomerID, IDNumber)

但考虑到身份证号与客户是一对一关系,且业务上需要频繁同时查询这些信息,保留当前结构是合理的

最终决定保持当前设计,因为它已满足3NF(IDNumber实际上是候选键)

(5)Reservations 表

初始状态在第一范式(1NF),所有属性都是原子值

已经在第二范式(2NF)

主键是ReservationID

所有非主属性完全依赖于整个主键

第三范式(3NF)分析

TotalPrice = Room.Price * Days,存在计算依赖

解决方案选择:

1.移除TotalPrice,每次查询时计算

2.保留作为冗余字段,通过触发器/应用层维护

选择保留TotalPrice,因为:

价格可能需要记录历史值(房间价格可能变化)

提高查询性能

通过触发器确保数据一致性

6. 物理模型设计

表6 HotelTypes 设计表

列名

类型

长度

约束

索引

TypeID

INT

N/A

主键, 自动增长

PK

TypeName

VARCHAR

50

不为空

idx_TypeName

StarLevel

INT

N/A

1-5星

NULL

Description

TEXT

N/A

NULL

NULL

表7 Hotels 设计表

列名

类型

长度

约束

索引

HotelID

INT

N/A

主键, 自动增长

PK

HotelName

VARCHAR

100

不为空

idx_HotelName

Address

VARCHAR

200

NULL

NULL

TypeID

INT

N/A

外键

FK

Contact

VARCHAR

50

NULL

NULL

表8 Rooms 设计表

列名

类型

长度

约束

索引

RoomID

INT

N/A

主键, 自动增长

PK

HotelID

INT

N/A

外键

FK

RoomType

VARCHAR

50

不为空

idx_RoomType

Price

DECIMAL

10,2

不为空

NULL

Features

TEXT

N/A

NULL

NULL

TotalRooms

INT

N/A

不为空

NULL

AvailableRooms

INT

N/A

不为空

NULL

表9 Customers 设计表

列名

类型

长度

约束

索引

CustomerID

INT

N/A

主键, 自动增长

PK

Name

VARCHAR

50

不为空

idx_Name

Gender

ENUM

('男','女')

NULL

NULL

Contact

VARCHAR

50

NULL

NULL

IDNumber

VARCHAR

18

唯一

idx_IDNumber

表10 Reservations 设计表

列名

类型

类型

约束

索引

ReservationID

INT

N/A

主键, 自动增长

PK

CustomerID

INT

N/A

外键

FK

RoomID

INT

N/A

外键

FK

CheckInDate

DATE

N/A

不为空

idx_CheckInDate

CheckOutDate

DATE

N/A

不为空

NULL

Days

INT

N/A

不为空

NULL

Status

ENUM

('预订中','已入住','已取消')

默认'预订中'

NULL

TotalPrice

DECIMAL

10,2

不为空

NULL

7. 实现

表的创建: 

  1.  -- 创建酒店类型表
  2. CREATE TABLE HotelTypes (
  3.     TypeID INT AUTO_INCREMENT PRIMARY KEY,
  4.     TypeName VARCHAR(50) NOT NULL,
  5.     StarLevel INT CHECK (StarLevel BETWEEN 1 AND 5),
  6.     Description TEXT,
  7.     INDEX idx_TypeName (TypeName)
  8. );
  9. -- 创建酒店表
  10. CREATE TABLE Hotels (
  11.     HotelID INT AUTO_INCREMENT PRIMARY KEY,
  12.     HotelName VARCHAR(100) NOT NULL,
  13.     Address VARCHAR(200),
  14.     TypeID INT,
  15.     Contact VARCHAR(50),
  16.     FOREIGN KEY (TypeID) REFERENCES HotelTypes(TypeID),
  17.     INDEX idx_HotelName (HotelName)
  18. );
  19. -- 创建房间表
  20. CREATE TABLE Rooms (
  21.     RoomID INT AUTO_INCREMENT PRIMARY KEY,
  22.     HotelID INT,
  23.     RoomType VARCHAR(50) NOT NULL,
  24.     Price DECIMAL(10, 2) NOT NULL,
  25.     Features TEXT,
  26.     TotalRooms INT NOT NULL,
  27.     AvailableRooms INT NOT NULL,
  28.     FOREIGN KEY (HotelID) REFERENCES Hotels(HotelID),
  29.     INDEX idx_RoomType (RoomType)
  30. );
  31. -- 创建客户表
  32. CREATE TABLE Customers (
  33.     CustomerID INT AUTO_INCREMENT PRIMARY KEY,
  34.     Name VARCHAR(50) NOT NULL,
  35.     Gender ENUM('男', '女'),
  36.     Contact VARCHAR(50),
  37.     IDNumber VARCHAR(18) UNIQUE,
  38.     INDEX idx_Name (Name),
  39.     INDEX idx_IDNumber (IDNumber)
  40. );
  41. -- 创建预定表
  42. CREATE TABLE Reservations (
  43.     ReservationID INT AUTO_INCREMENT PRIMARY KEY,
  44.     CustomerID INT NOT NULL,
  45.     RoomID INT NOT NULL,
  46.     CheckInDate DATE NOT NULL,
  47.     CheckOutDate DATE NOT NULL,
  48.     Days INT NOT NULL,
  49.     Status ENUM('预订中','已入住','已取消') DEFAULT '预订中',
  50.     TotalPrice DECIMAL(10,2) NOT NULL,
  51.     CONSTRAINT chk_valid_dates CHECK (CheckOutDate > CheckInDate),
  52.     CONSTRAINT chk_positive_days CHECK (Days > 0),
  53.     FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID),
  54.     FOREIGN KEY (RoomID) REFERENCES Rooms(RoomID)
  55. ) ENGINE=InnoDB;

触发器设计:

1.预订时减少可用房间数

  1. DELIMITER ;;
  2. CREATE TRIGGER AfterReservationInsert AFTER INSERT ON Reservations
  3. FOR EACH ROW
  4. BEGIN
  5.     IF NEW.Status = '预订中' THEN
  6.         UPDATE Rooms
  7.         SET AvailableRooms = AvailableRooms - 1
  8.         WHERE RoomID = NEW.RoomID;
  9.     END IF;
  10. END;;
  11. DELIMITER ;

2.取消预订时增加可用房间数 

  1. DELIMITER ;;
  2. CREATE TRIGGER AfterReservationUpdate AFTER UPDATE ON Reservations
  3. FOR EACH ROW
  4. BEGIN
  5.     IF OLD.Status = '预订中' AND NEW.Status = '已取消' THEN
  6.         UPDATE Rooms
  7.         SET AvailableRooms = AvailableRooms + 1
  8.         WHERE RoomID = NEW.RoomID;
  9.     END IF;
  10. END;;
  11. DELIMITER ; 

3. 删除预订时增加可用房间数

  1. DELIMITER ;;
  2. CREATE TRIGGER BeforeReservationDelete BEFORE DELETE ON Reservations
  3. FOR EACH ROW
  4. BEGIN
  5.     IF OLD.Status = '预订中' THEN
  6.         UPDATE Rooms
  7.         SET AvailableRooms = AvailableRooms + 1
  8.         WHERE RoomID = OLD.RoomID;
  9.     END IF;
  10. END;;
  11. DELIMITER ;

4. 使用触发器自动计算总价

  1. DELIMITER //
  2. CREATE TRIGGER set_reservation_totalprice
  3. BEFORE INSERT ON Reservations
  4. FOR EACH ROW
  5. BEGIN
  6.     DECLARE room_price DECIMAL(10,2);
  7.     
  8.     -- 获取房间价格
  9.     SELECT Price INTO room_price FROM Rooms WHERE RoomID = NEW.RoomID;
  10.     
  11.     -- 计算总价
  12.     SET NEW.TotalPrice = room_price * NEW.Days;
  13. END//
  14. DELIMITER ;

存储过程设计(事务处理实现):

1.查询可用房间

  1. DELIMITER ;;
  2. CREATE PROCEDURE GetAvailableRooms(
  3.     IN hotel_id INT,
  4.     IN room_type VARCHAR(50),
  5.     IN check_in DATE,
  6.     IN check_out DATE
  7. )
  8. BEGIN
  9.     SELECT r.RoomID, r.RoomType, r.Price, r.Features, r.AvailableRooms,
  10.            h.HotelName, h.Address, ht.TypeName, ht.StarLevel
  11.     FROM Rooms r
  12.     JOIN Hotels h ON r.HotelID = h.HotelID
  13.     JOIN HotelTypes ht ON h.TypeID = ht.TypeID
  14.     WHERE (hotel_id IS NULL OR r.HotelID = hotel_id)
  15.       AND (room_type IS NULL OR r.RoomType LIKE CONCAT('%', room_type, '%'))
  16.       AND r.AvailableRooms > 0
  17.       AND NOT EXISTS (
  18.           SELECT 1 FROM Reservations res
  19.           WHERE res.RoomID = r.RoomID
  20.             AND res.Status != '已取消'
  21.             AND (
  22.                 (check_in BETWEEN res.CheckInDate AND res.CheckOutDate)
  23.                 OR (check_out BETWEEN res.CheckInDate AND res.CheckOutDate)
  24.                 OR (res.CheckInDate BETWEEN check_in AND check_out)
  25.             )
  26.       );
  27. END;;
  28. DELIMITER ; 

2.创建预订

  1. DELIMITER //
  2. CREATE PROCEDURE MakeReservation(
  3.     IN p_CustomerID INT,
  4.     IN p_RoomID INT,
  5.     IN p_CheckInDate DATE,
  6.     IN p_CheckOutDate DATE
  7. )
  8. BEGIN
  9.     DECLARE v_Price DECIMAL(10,2);
  10.     DECLARE v_Available INT;
  11.     DECLARE v_ConflictCount INT;
  12.     DECLARE EXIT HANDLER FOR SQLEXCEPTION
  13.     BEGIN
  14.         ROLLBACK;
  15.         SELECT '预订失败:系统错误' AS Result;
  16.     END;
  17.     
  18.     START TRANSACTION;
  19.     
  20.     -- 锁定房间记录防止并发修改
  21.     SELECT Price, AvailableRooms INTO v_Price, v_Available
  22.     FROM Rooms WHERE RoomID = p_RoomID FOR UPDATE;
  23.     
  24.     -- 检查时间冲突
  25.     SELECT COUNT(*) INTO v_ConflictCount
  26.     FROM Reservations
  27.     WHERE RoomID = p_RoomID
  28.     AND Status != '已取消'
  29.     AND p_CheckInDate < CheckOutDate
  30.     AND p_CheckOutDate > CheckInDate;
  31.     
  32.     -- 验证业务规则
  33.     IF v_Available <= 0 THEN
  34.         ROLLBACK;
  35.         SELECT '预订失败:房间已满' AS Result;
  36.     ELSEIF v_ConflictCount > 0 THEN
  37.         ROLLBACK;
  38.         SELECT '预订失败:时间冲突' AS Result;
  39.     ELSE
  40.         -- 插入预订记录
  41.         INSERT INTO Reservations (CustomerID, RoomID, CheckInDate, CheckOutDate, Days, Status, TotalPrice)
  42.         VALUES (p_CustomerID, p_RoomID, p_CheckInDate, p_CheckOutDate,
  43.                DATEDIFF(p_CheckOutDate, p_CheckInDate), '预订中',
  44.                v_Price * DATEDIFF(p_CheckOutDate, p_CheckInDate));
  45.         
  46.         -- 更新可用房间数
  47.         UPDATE Rooms SET AvailableRooms = AvailableRooms - 1 WHERE RoomID = p_RoomID;
  48.         
  49.         COMMIT;
  50.         SELECT '预订成功' AS Result;
  51.     END IF;
  52. END //
  53. DELIMITER ;

3. 取消预订 

  1. DELIMITER ;;
  2. CREATE PROCEDURE CancelReservation(
  3.     IN reservation_id INT
  4. )
  5. BEGIN
  6.     UPDATE Reservations
  7.     SET Status = '已取消'
  8.     WHERE ReservationID = reservation_id AND Status = '预订中';
  9.     
  10.     IF ROW_COUNT() > 0 THEN
  11.         SELECT '取消预订成功' AS Message;
  12.     ELSE
  13.         SELECT '取消预订失败,可能已经入住或已取消' AS Message;
  14.     END IF;
  15. END;;
  16. DELIMITER ;  

4. 统计酒店收入

  1. DELIMITER ;;
  2. CREATE PROCEDURE GetHotelRevenue(
  3.     IN hotel_id INT,
  4.     IN start_date DATE,
  5.     IN end_date DATE
  6. )
  7. BEGIN
  8.     SELECT h.HotelID, h.HotelName,
  9.            COUNT(r.ReservationID) AS ReservationCount,
  10.            SUM(r.TotalPrice) AS TotalRevenue
  11.     FROM Hotels h
  12.     LEFT JOIN Rooms rm ON h.HotelID = rm.HotelID
  13.     LEFT JOIN Reservations r ON rm.RoomID = r.RoomID
  14.     WHERE (hotel_id IS NULL OR h.HotelID = hotel_id)
  15.       AND (start_date IS NULL OR r.CheckInDate >= start_date)
  16.       AND (end_date IS NULL OR r.CheckInDate <= end_date)
  17.       AND r.Status != '已取消'
  18.     GROUP BY h.HotelID, h.HotelName;
  19. END;;
  20. DELIMITER ; 

5. 客户预订历史 

  1. DELIMITER ;;
  2. CREATE PROCEDURE GetCustomerReservations(
  3.     IN customer_id INT
  4. )
  5. BEGIN
  6.     SELECT r.ReservationID, r.CheckInDate, r.CheckOutDate, r.Days, r.Status, r.TotalPrice,
  7.            rm.RoomType, rm.Price, rm.Features,
  8.            h.HotelName, h.Address, h.Contact,
  9.            ht.TypeName, ht.StarLevel
  10.     FROM Reservations r
  11.     JOIN Rooms rm ON r.RoomID = rm.RoomID
  12.     JOIN Hotels h ON rm.HotelID = h.HotelID
  13.     JOIN HotelTypes ht ON h.TypeID = ht.TypeID
  14.     WHERE r.CustomerID = customer_id
  15.     ORDER BY r.CheckInDate DESC;
  16. END;;
  17. DELIMITER ; 

视图设计:

1.可用房间视图

  1. CREATE VIEW AvailableRoomsView AS
  2. SELECT r.RoomID, r.RoomType, r.Price, r.Features, r.AvailableRooms,
  3.        h.HotelID, h.HotelName, h.Address, h.Contact,
  4.        ht.TypeName, ht.StarLevel
  5. FROM Rooms r
  6. JOIN Hotels h ON r.HotelID = h.HotelID
  7. JOIN HotelTypes ht ON h.TypeID = ht.TypeID
  8. WHERE r.AvailableRooms > 0;

2. 当前预订视图 

  1. CREATE VIEW CurrentReservations AS
  2. SELECT r.ReservationID, r.CheckInDate, r.CheckOutDate, r.Days, r.Status, r.TotalPrice,
  3.        c.CustomerID, c.Name, c.Gender, c.Contact,
  4.        rm.RoomID, rm.RoomType, rm.Price AS RoomPrice,
  5.        h.HotelName, h.Address,
  6.        ht.TypeName, ht.StarLevel
  7. FROM Reservations r
  8. JOIN Customers c ON r.CustomerID = c.CustomerID
  9. JOIN Rooms rm ON r.RoomID = rm.RoomID
  10. JOIN Hotels h ON rm.HotelID = h.HotelID
  11. JOIN HotelTypes ht ON h.TypeID = ht.TypeID
  12. WHERE r.Status IN ('预订中', '已入住')
  13. ORDER BY r.CheckInDate;

数据的录入:

  1. -- 插入酒店类型数据
  2. INSERT INTO HotelTypes (TypeName, StarLevel, Description) VALUES
  3. ('经济型', 2, '经济实惠,基本设施齐全'),
  4. ('舒适型', 3, '舒适住宿,设施较为完善'),
  5. ('豪华型', 4, '高端住宿,设施豪华'),
  6. ('奢华型', 5, '顶级住宿,极致体验');
  7. -- 插入酒店数据
  8. INSERT INTO Hotels (HotelName, Address, TypeID, Contact) VALUES
  9. ('如家酒店', '北京市朝阳区建国路88号', 1, '010-12345678'),
  10. ('汉庭酒店', '上海市浦东新区张江路100号', 1, '021-87654321'),
  11. ('全季酒店', '广州市天河区天河路200号', 2, '020-11223344'),
  12. ('希尔顿酒店', '深圳市福田区福华路1号', 3, '0755-55667788'),
  13. ('四季酒店', '杭州市西湖区西湖大道1号', 4, '0571-99887766');
  14. -- 插入房间数据
  15. INSERT INTO Rooms (HotelID, RoomType, Price, Features, TotalRooms, AvailableRooms) VALUES
  16. (1, '标准大床房', 299, '1.8米大床,免费WiFi', 50, 50),
  17. (1, '标准双床房', 329, '2张1.2米床,免费WiFi', 30, 30),
  18. (2, '商务大床房', 359, '1.8米大床,办公桌', 40, 40),
  19. (2, '家庭房', 429, '1张1.8米大床+1张1.2米小床', 20, 20),
  20. (3, '豪华大床房', 599, '2米大床,浴缸', 35, 35),
  21. (3, '行政套房', 899, '独立客厅,迷你吧', 15, 15),
  22. (4, '海景大床房', 1299, '海景,浴缸,迷你吧', 25, 25),
  23. (4, '总统套房', 5999, '200平米,私人管家', 5, 5),
  24. (5, '湖景套房', 1999, '西湖景观,豪华设施', 20, 20),
  25. (5, '花园别墅', 3999, '私人花园,独立泳池', 10, 10);
  26. -- 插入客户数据
  27. INSERT INTO Customers (Name, Gender, Contact, IDNumber) VALUES
  28. ('张三', '男', '13800138000', '110101199001011234'),
  29. ('李四', '女', '13900139000', '110102199002021235'),
  30. ('王五', '男', '13700137000', '110103199003031236'),
  31. ('赵六', '女', '13600136000', '110104199004041237'),
  32. ('钱七', '男', '13500135000', '110105199005051238'); 

8. 测试

1.插入数据的测试

酒店类型表

图4 酒店类型数据

酒店表

图5 酒店数据

房间表

图6 房间数据

客户表

图7 客户数据

2. 酒店类型管理测试

添加酒店类型,插入成功,返回1行受影响,查询返回1条记录,包含正确的类型信息

图8 酒店类型管理测试

3. 酒店管理测试

3.1:添加酒店

先添加类型(先前不存在),再添加酒店,查询验证,插入成功,返回1行受影响,查询返回1条记录,包含正确的酒店信息

图9 添加酒店测试

3.2:添加酒店时外键约束验证

插入失败,返回外键约束错误

图10 验证外键约束

4. 房间管理测试

4.1:添加房间

插入成功,返回1行受影响,查询返回1条记录,包含正确的房间信息

图11 添加房间测试

4.2:房间可用数不能大于总数

刚开始测试成功插入了一条房间数据,但这条记录存在问题:AvailableRooms(10)大于TotalRooms(5),这违反了业务逻辑。首先删除这条错误记录,然后通过触发器防止此类问题,添加检查约束,重新插入正确数据,验证约束是否生效

图12 房间测试

5. 客户管理测试

5.1:添加客户

图13 添加客户测试

5.2:添加重复身份证客户

先添加测试客户(先前不存在),尝试插入相同身份证,插入失败,返回唯一约束错误

图14 添加重复客户测试

6.预订管理测试

6.1:创建有效预订

解决 TotalPrice 字段缺失默认值错误, 使用触发器自动计算总价, 确保价格始终正确计算,即使房间价格变化,预订记录仍保留原始价格,避免人工计算错误

图15 预订测试

预订创建成功,房间可用数减少1,TotalPrice自动计算正确(通过触发器)

图16 预订测试

6.2:创建冲突预订

尝试创建重叠时间段的预订,系统允许了时间重叠的预订插入。这会导致同一房间在同一时间段被重复预订, 使用存储过程封装预订逻辑

图17 创建冲突预订

6.3:取消预订

存在状态为"预订中"的预订(如ReservationID=1), 记录当前可用房间数, 取消预订, 验证可用房间数, 更新成功,房间可用数增加1

图18 取消预订测试

7. 查询功能测试

7.1:精确查询客户预订

验证能按客户ID精确查询, 返回该客户的所有预订记录,结果集包含完整的预订信息

图19 精确查询

7.2:模糊查询酒店

验证能按名称模糊查询酒店, 返回名称包含"测试"的所有酒店,结果集包含酒店完整信息

图20 模糊查询

7.3:统计查询酒店收入

验证能统计指定时间段收入,返回指定酒店在2024年的预订数量和总收入,数据应与实际预订记录一致

图21 统计酒店收入

8.事务处理测试

8.1:并发预订测试

图22 并发预订测试

8.2事务回滚测试

验证事务失败时数据一致性, 整个事务回滚,预订记录未创建,房间可用数不变

图23 事务回滚测试

9. 边界条件测试

9.1:房间订满测试

图24 房间订满测试

9.2:无效日期测试

刚成功插入了一条退房日期早于入住日期的预订记录(Days为-2),表结构没有强制CheckOutDate必须大于CheckInDate的约束,修改Reservations表结构,添加检查约束,添加触发器验证修复已存在的无效数据

图25 无效日期测试

图26 无效日期测试

10.可视化界面

图27 可视化界面

9. 总结

当我第一次接触数据库课程设计时,我以为只需要掌握基本的SQL语法和表设计就能完成任务。但随着项目深入,我逐渐意识到一个健壮的数据库系统远比想象中复杂。酒店预订系统看似简单,实则涉及复杂的数据关系、严格的业务规则和并发的用户访问,这完全超出了我的最初预期。

1.系统设计演进过程

在开始编码前,我花费了大量时间研究酒店管理业务流程,绘制了详细的ER图和系统架构。我的初始设计包含三个核心表:Hotels、Rooms和Reservations。为确保数据完整性,我设置了外键约束和基础索引,但很快在测试中暴露了严重问题。

2.遇到的问题与突破

初期系统竟然允许插入CheckOutDate < CheckInDate的非法数据。通过分析,我发现:MySQL默认不强制CHECK约束,应用层验证缺失,触发器未覆盖所有情况

Reservations 表中的 TotalPrice 字段没有默认值且不能为 NULL,但我的 INSERT 语句中没有提供该值,所以后面使用触发器自动计算总价,能确保:价格始终正确计算,即使房间价格变化,预订记录仍保留原始价格,避免人工计算错误

3. 经验教训总结

设计阶段的重要性:完善的ER设计能减少后期修改成本,业务规则要尽早转化为数据库约束,索引策略需要提前规划。

这个酒店预订系统的开发过程,让我深刻理解了"数据库是应用的核心"这句话的含义。从最初的天真设想到最终完成一个健壮的系统,这段经历不仅提升了我的技术水平,更培养了我作为开发者的系统思维和工程素养。每当看到系统稳定处理大量并发请求时,我都为这个过程中的成长感到自豪。这远不止是一个课程设计,而是我技术生涯的重要里程碑。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐