
简介面向数据库课程设计学生这套航空订票系统设计文档覆盖需求分析、系统结构数据设计、逻辑结构设计、系统功能模块设计到项目总结的完整流程可作为课程报告撰写与数据库建模的参考范本。压缩包共1个doc文档体积约335KB报告包含E-R图、关系模式、数据流程图并详细设计了旅客、航班、预订、支付等核心实体及其关联同时给出数据表字段描述、程序功能模块和系统功能分析有助于理解数据库概念建模、关系规范化与实际业务逻辑的映射。文档还梳理了需求规定、功能模块划分和实现流程对完成课程设计中的文档编写与数据库结构设计有直接帮助。目前已有97人学习浏览适合正在开展航空订票类数据库课程设计或希望系统掌握订票系统架构的学生参考使用。1. 最新航空订票系统一份数据库课程设计文档背后的完整技术栈打开这份《最新航空订票系统数据库课程设计.doc》之前先想清楚一个事实绝大多数人手里拿到的不是能直接运行的软件而是一份要交给老师评分的文档外加一个从零开始搭起来的订票系统。这个题目在数据库课程设计里属于经典中的经典覆盖了从 ER 图、关系模式、范式优化到 SQL 增删改查、视图、存储过程、触发器、并发控制的全部考点。它同时也是入门级项目里最容易“把代码写出来但答辩说不上原理”的题目因为业务不复杂但数据库设计的好坏一眼就能看出来。这篇笔记要解决的问题是把这份课程设计文档变成一套能在 MySQL 上复现、能自圆其说、能应对老师追问的完整落地方案。适合正在做数据库课设大作业的在校生也适合需要快速上手关系型数据库设计流程的转行开发者。2. 把需求拆成关系模式航班、乘客与订单的边界划分2.1 先定业务边界再画 ER 图订票系统有哪些实体必须出现航空订票系统如果只是“订票”两个字做出来的表结构很容易走偏。常见做法是先把业务拆成三个闭环航班信息管理、乘客信息管理、订单流转下单、支付、退票、改签。围绕这三个闭环实体至少要包括航班、乘客、订单、订单明细、舱位价格。为什么订单明细不能省——一次下单可能同时买两张票而两张票的舱位价格可能不同如果直接把票价字段塞进订单表一个月后想统计“经济舱总收入”就得解析字符串或者靠猜这就是典型的表设计没做干净。实体确定后属性也要跟着业务走。航班的属性里航班号、起降时间、起降机场、机型、总座位数这几个是必须的剩余座位数建议别落表放到后面用视图或实时计算去实现否则每次订票都要同步修改两个字段出现不一致的概率很高。乘客属性里身份证号是天然的候选键但要考虑一个现实课程设计里可能要用测试数据批量插入身份证号既做业务键又做主键会让后续修改非常麻烦我一般会把它设成 UNIQUE 约束而不是 PRIMARY KEY。订单属性里最容易被忽略的是订单状态建议用 TINYINT 存数字0 代表已创建、1 代表已支付、2 代表已出票、3 代表已退票不要在数据库里存中文状态排序和统计都会难受。2.2 从 ER 图到关系模式五张表的接口设计与外键方向把 ER 图转成关系模式时最容易翻车的点是外键的方向。航空订票系统的关系其实很清楚一个乘客可以有多张订单一张订单可以包含多个乘客家庭出游一次买三张票所以需要三张核心表加一张中间表。航班和订单之间是“一对多”外键加在订单表乘客和订单之间是“多对多”通过订单明细表去桥接。这里有个经验不要把乘客直接挂在订单表上订单表只存“谁下的单、什么时候下的单、总价、状态”具体每个人坐哪个航班、什么舱位、多少钱全部放明细表。字段类型的选择也是数据库课程设计的评分点。航班编号用 CHAR(6) 而不是 INT因为航班号像 CA1831 这种是带字母的编码INT 存不了日期和时间用 DATETIME 而不是拆成两个 VARCHAR金额用 DECIMAL(10,2) 而不是 FLOAT原因应该不用多讲结算场景出现浮点误差直接是事故。一张能拿到高分的表光看字段类型就能看出设计者有没有做过真实项目。关系模式定稿之后还要写清楚每个关系的函数依赖和规范化说明。课程设计文档里这一节占的篇幅不多但分值不小重点是解释“为什么这张表已经在第三范式”订单明细表的主键是订单编号加乘客编号的组合键所有非主属性完全依赖于这个组合键不存在部分依赖这是第二范式同时舱位定价、折扣这些字段只依赖航班和舱位不会在明细表里出现冗余这是第三范式。老师的追问一般就集中在范式这里能用一句话讲清楚就可以过关。3. 建库建表与约束MySQL 8.0 上跑通最小航班库3.1 建库脚本字符集、排序规则与存储引擎的选型数据库选型上课程设计最常见的组合是 MySQL 8.x 加 Navicat 或 MySQL Workbench。“最新航空订票系统”这个标题里的“最新”更多是指业务形态不是指什么冷门数据库。建库这一步字符集和排序规则直接决定后面会不会出现中文乱码和索引失效的问题。UTF-8 在 MySQL 里是个历史遗留的大坑MySQL 8.0 里 utf8mb4 才是真正的四字节 UTF-8utf8 只是 utf8mb3 的别名遇到生僻字或 Emoji 直接撑爆字段排序规则用 utf8mb4_unicode_ci 而不是 utf8mb4_general_ci前者对中文和多语言的排序更符合 Unicode 标准。以下是课程设计里可以直接复用的建库脚本。DROP DATABASE IF EXISTS air_ticket_db; CREATE DATABASE air_ticket_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE air_ticket_db; CREATE TABLE flight ( flight_id CHAR(6) NOT NULL COMMENT 航班内部编号, flight_no VARCHAR(10) NOT NULL COMMENT 航班号如 CA1831, origin CHAR(3) NOT NULL COMMENT 出发机场三字码, destination CHAR(3) NOT NULL COMMENT 到达机场三字码, depart_time DATETIME NOT NULL COMMENT 计划起飞时间, arrive_time DATETIME NOT NULL COMMENT 计划到达时间, aircraft VARCHAR(20) NOT NULL DEFAULT B737 COMMENT 机型, total_seats SMALLINT NOT NULL COMMENT 总座位数, PRIMARY KEY (flight_id), UNIQUE KEY uk_flight_no (flight_no, depart_time), KEY idx_route (origin, destination) ) ENGINE InnoDB;这段建表脚本里的每个参数都有具体用途。flight_no加depart_time做联合唯一键是为了避免同一天同一航班号被插入两次这在订票业务里属于完整性问题idx_route是为“查某个航线的所有航班”这种高频查询准备的不加索引的话数据库全表扫描会随着测试数据量增大而越来越慢COMMENT注释虽然不影响运行但课程设计评分时老师看表结构就能理解字段含义比在文档里单独解释省事得多。存储引擎必须用 InnoDB不要用 MyISAM理由在 5.3 节讲死锁和事务时会直接体现出来。3.2 乘客表与订单表的约束设计CHECK、外键与默认值4. 核心业务 SQL订票、退票与改签的增删改查闭环4.1 订票存储过程事务边界与行锁加锁顺序Airline reservation system, database course design, Chen definition, physical design, database connection pool, transaction, deadlock, view, stored procedure, trigger. ## 4. 核心业务 SQL订票、退票与改签的增删改查闭环4.1 订票存储过程事务边界与行锁加锁顺序航司的核心业务循环就是尽力卖光每个航班的有效座位而数据库面试官最爱问的问题之一就是“订票时怎样避免最后一张票被两个人同时抢到”。如果不假思索地在业务代码里先 SELECT 余票数再 INSERT 订单高并发下几乎必挂因为这两条 SQL 之间存在时间窗口两个会话可能同时读到余票为 1然后各自插入订单最后航班冗余销售。下面存储过程用“先查后写且锁定”的方式处理这个问题。DELIMITER // DROP PROCEDURE IF EXISTS sp_book_ticket; CREATE PROCEDURE sp_book_ticket ( IN p_flight_id CHAR(6), IN p_passenger_id CHAR(18), IN p_order_id INT, IN p_cabin_class VARCHAR(10), OUT p_success TINYINT ) SQL SECURITY INVOKER BEGIN DECLARE v_remaining INT DEFAULT 0; START TRANSACTION; -- 锁定航班行防止其他会话同时读到相同余票 SELECT remaining_seats INTO v_remaining FROM flight WHERE flight_id p_flight_id FOR UPDATE; IF v_remaining 0 THEN SET p_success 0; ROLLBACK; ELSE INSERT INTO booking_detail (order_id, passenger_id, cabin_class, ticket_price) SELECT p_order_id, p_passenger_id, p_cabin_class, price FROM cabin_price WHERE flight_id p_flight_id AND cabin_class p_cabin_class; IF ROW_COUNT() 0 THEN SET p_success 0; ROLLBACK; ELSE UPDATE flight SET remaining_seats remaining_seats - 1 WHERE flight_id p_flight_id; SET p_success 1; COMMIT; END IF; END IF; END // DELIMITER ;这段存储过程把并发安全性放在了数据库层面而不是业务代码层面。FOR UPDATE加的是行级排他锁当第一个会话执行到这一句时MySQL 会对该航班的行加锁直到事务提交或回滚第二个会话再来执行同一个存储过程会在同一行SELECT ... FOR UPDATE上等待直到第一个会话释放锁。这样“先到先得”的逻辑就完全由数据库保证了。存储过程的参数按用途分成三个维度业务键、关联键和输出参数。p_flight_id是航班内部编码p_passenger_id用乘客身份证号作为关联键而不是依赖自增主键这样后续退票、改签时业务语义清晰p_success用 OUT 类型返回结果便于 JDBC 或 Python 调用端直接判断成功与否。唯一要留意的是INSERT INTO ... SELECT中间如果查不到对应舱位价格会静默插入零行并返回成功所以代码里必须判断ROW_COUNT()。真正做课程设计时我建议把这个存储过程当成版本的“代码库模板”原因是事务要明确、异常要回滚、业务结果要有显式的成功标志这三点在架构设计一致性上至关重要而存储过程中过程式的IF/ELSE逻辑恰好能帮助初学者理解数据库内部流程控制——这是单独用 Java 写 DAO 很难看清的攻坚难点。4.2 退票与改签 SQL触发器的一致性和时点问题4.3 视图与离线报表把查询语义变成可复用的对象5. 并发与排查死锁、连接池和课程设计最常翻车的 5 个坑5.1 现象余票变负数原因丢失更新5.2 现象MySQL 8 连接失败原因驱动版本与时区参数5.3 现象订单删除失败原因外键约束与删除顺序5.4 现象插入中文乱码原因连接参数没有指定字符集5.5 现象死锁原因多事务加锁顺序不一致6. 收尾技巧数据校验脚本与课程设计答辩自检清单本文还有配套的精品资源点击获取