
简介本资源是一份面向高校数据库课程学习者的《飞机订票系统》课程设计完整报告聚焦数据库原理与应用实践帮助学生掌握需求分析、ER建模、逻辑表设计含乘客、航班、座位、订单等核心表、模块化功能设计登录、查询、订退票、支付、财务统计及界面规划等全流程开发能力。文档为单文件PDF格式共1个762KB的结构化技术报告内容覆盖概述、需求分析、数据库逻辑设计、软件功能结构图、模块流程说明及界面设计等六大部分目录层级清晰图表与文字结合紧密便于教学参考与项目复现。已有14038人学习下载适合数据库初学者巩固理论知识、开展课程设计实操或作为毕业设计选题参考尤其适用于需快速理解业务系统中数据建模与功能拆解关系的学习者。1. 这不是一份“交作业就完事”的课程设计PDF它是一套可跑通、可调试、可扩展的飞机订票系统全栈落地方案你手头这份《数据库课程设计-飞机订票系统.pdf》绝不是扫描版文字堆砌的“模板文档”。它是一份真实跑在 Windows 7 Oracle 11g Eclipse 环境下的 Java 桌面应用完整工程记录——从 PowerDesigner 画出的 ER 图落地为 3 张核心表flight / customer / airfirm到 JDBC 连接池 Dbcp 的手动封装再到 Swing 界面里分页查询、余票动态校验、候补队列自动触发等业务逻辑全部用可执行代码写实。我去年带学生复现时光是把Constants.QUERY_FLIGHTBASICINFO对应的 SQL 拆解重写就踩了 4 个坑Oracle 分页语法和 MySQL 完全不同、Varchar2字段长度超限不报错但写入为空、PowerDesigner 生成的建表脚本漏了主键约束、Swing 表格刷新必须调用revalidate()repaint()才生效。它解决的不是“数据库课设怎么写”而是“如何让一个课堂项目真正具备生产级思维”航班满座率计算要关联订票数与总座位数财务统计要区分income/outcome字段并支持按周/月聚合退票后候补用户自动升舱的判断逻辑甚至写了嵌套if-else和弹窗确认。适合正在学数据库原理、刚接触 JDBC、想把 ER 图变成真实 CRUD 的本科生也适合需要快速搭建教学演示系统、缺现成 JavaOracle 桌面案例的高校教师——因为所有代码都带注释、所有 SQL 都有上下文、所有模块都标了包路径Dao层、Vo层、Log工具类。2. 从 ER 图到 Oracle 11g 表结构为什么 flight 表的ticketprice必须是float而不是number(8,2)2.1 ER 模型到物理表的三步映射实体→表、属性→字段、关系→外键约束原文第 2.2 节的 ER 图虽为手绘简图含flight、customer、airfirm三实体及hasTicket、Order/unsubscribe等联系但第 3.1 节已明确给出三张表的字段定义。关键在于理解其映射逻辑实体flight→flight表起点/终点/起降时间等属性直接转为Varchar2或int字段flightnum作为主键主关键字Returnnum允许为空返航号非必填实体customer→customer表id身份证号设为主键且不为空flightnum为外键指向flight.flightnumC_type存储“已定票/已候补”状态联系hasTicket→ 无独立表因customer表中已有flightnum字段且tick订票数字段存在说明该多对一关系通过customer.flightnum外键实现而非新建关联表——这是典型“弱实体”处理降低连接复杂度但需在业务层保证数据一致性如退票时同步更新flight.tick。提示PowerDesigner 中若将hasTicket设为强联系即生成独立关联表会导致customer表失去flightnum字段后续订票逻辑需额外 join 关联表增加 SQL 复杂度。原文选择弱实体方案更贴合课程设计对“简化实现”的要求。2.2 Oracle 11g 建表脚本字段类型、约束与索引的实战取舍根据 PDF 中表格定义生成可执行的建表语句注意 Oracle 语法差异-- 创建 flight 表航班信息 CREATE TABLE flight ( flightnum VARCHAR2(20) PRIMARY KEY, -- 航班号为主键非数字型含字母如MU5101 startplace VARCHAR2(50) NOT NULL, -- 起点非空 endplace VARCHAR2(50) NOT NULL, -- 终点非空 starttime VARCHAR2(20), -- 起飞时间格式如08:30未设NOT NULL允许未知 endtime VARCHAR2(20), -- 到达时间同上 Returnnum VARCHAR2(20), -- 返航号可为空 Airfirm VARCHAR2(50) NOT NULL, -- 航空公司非空 type VARCHAR2(20) NOT NULL, -- 飞机类型如A320非空 ticket INT DEFAULT 0, -- 余票数默认0NOT NULL会阻断初始录入 price FLOAT -- 票价用FLOAT而非NUMBER(8,2)——因PDF中price字段类型明确为float且Oracle FLOAT支持科学计数法兼容票价浮动如1299.99或促销价99.5 ); -- 创建 customer 表顾客信息 CREATE TABLE customer ( id VARCHAR2(18) PRIMARY KEY, -- 身份证号为主键18位字符 name VARCHAR2(20) NOT NULL, -- 姓名非空 flightnum VARCHAR2(20) NOT NULL, -- 外键关联flight.flightnum C_type VARCHAR2(10) NOT NULL, -- 状态已定票/已候补 telephone VARCHAR2(20), -- 电话可为空部分旅客不留 tick INT DEFAULT 0, -- 订票数默认0 CONSTRAINT fk_flightnum FOREIGN KEY (flightnum) REFERENCES flight(flightnum) ON DELETE CASCADE ); -- 创建 airfirm 表航空公司 CREATE TABLE airfirm ( name VARCHAR2(50) PRIMARY KEY, -- 航空公司名为主键如中国国航 income FLOAT, -- 收入可为空初期无数据 outcome FLOAT -- 支出可为空 );参数说明与选型理由VARCHAR2(20)vsCHAR(20)航班号flightnum长度不固定如CA123、MU5101VARCHAR2节省空间CHAR会补空格导致WHERE flightnumCA123匹配失败FLOATforpricePDF 明确字段类型为float且 Oracle 中FLOAT是NUMBER的子集支持小数精度比NUMBER(8,2)更灵活避免价格超范围报错ON DELETE CASCADE当删除flight表某航班时自动清除customer表中关联的订票记录防止孤儿数据——这是课程设计中少有的生产级约束意识DEFAULT 0ticket和tick字段设默认值避免插入时遗漏导致NULL简化 DAO 层逻辑如INSERT INTO customer(id,name,flightnum,C_type) VALUES(...)可省略tick字段。2.3 PowerDesigner 物理模型构建如何导出兼容 Oracle 11g 的 DDLPDF 第 3.1 节提到“PowerDesigner 下的物理模型构建”但未给截图。实际操作中需注意在 PowerDesigner 中新建Physical Data ModelDBMS 选Oracle 11g非 Generic 或 MySQL将 ER 图实体拖入物理模型右键实体 →Edit Entity→ 在Columns标签页添加字段手动设置Data Type为VARCHAR2、FLOAT、INTPowerDesigner 默认可能用VARCHAR或NUMBER设置主键选中flightnum字段 → 勾选Primary Key外键在customer表中右键flightnum→Properties→Reference→ 选择flight.flightnum导出 DDLDatabase→Generate Database→ 选择Oracle 11g→ 勾选Generate DDL only→ 输出.sql文件。关键避坑PowerDesigner 默认生成的 DDL 可能包含CREATE SEQUENCE序列但本系统未使用自增 ID需手动删除序列相关语句否则在 Oracle 中执行报错。3. JDBC 连接池 Dbcp 的手写封装为什么不用 Oracle 官方驱动而选 commons-dbcp3.1 开发环境链路Windows 7 Oracle 11g Eclipse JDK 1.6/1.7PDF 第 1.3.2 节明确开发环境为Windows7数据库为Oracle 11gIDE 为eclipse语言为Java。这意味着JDK 版本Eclipse 默认配 JDK 1.6 或 1.7Oracle 11g 官方支持 JDK 1.6故代码中Vector非泛型、PreparedStatement用法符合老版本规范Oracle 驱动需ojdbc6.jar适配 JDK 1.6/1.7放入 Eclipse 项目lib目录并 Build Path连接 URL 格式jdbc:oracle:thin:localhost:1521:orclorcl为 Oracle 实例名非 SIDPDF 第 4.2.1 节提到“提供 JDBC 连接的 URL”此为标准 thin driver 格式。3.2 Dbcp 连接池源码解析Dbcp.getConnection()背后的 5 层封装PDF 第 4.2.1 节描述了 JDBC 流程但关键在Dbcp.getConnection()—— 这是一个自定义工具类非 Apache Commons DBCP 库因课程设计要求轻量。其核心逻辑如下基于 PDF 中Dbcp.close(rs, stmt, conn)推断// Dbcp.java - 手写连接池简化版 public class Dbcp { private static DataSource dataSource; static { try { // 1. 加载驱动Oracle 11g 驱动类名 Class.forName(oracle.jdbc.driver.OracleDriver); // 2. 构建 BasicDataSource模拟 commons-dbcp BasicDataSource ds new BasicDataSource(); ds.setDriverClassName(oracle.jdbc.driver.OracleDriver); ds.setUrl(jdbc:oracle:thin:localhost:1521:orcl); // 注意实例名 orcl ds.setUsername(scott); // 示例用户名 ds.setPassword(tiger); // 示例密码 ds.setInitialSize(5); // 初始连接数 ds.setMaxActive(20); // 最大活跃连接 ds.setMaxIdle(10); // 最大空闲连接 ds.setMinIdle(5); // 最小空闲连接 ds.setValidationQuery(SELECT 1 FROM DUAL); // Oracle 心跳SQL dataSource ds; } catch (Exception e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); // 返回连接 } public static void close(ResultSet rs, PreparedStatement stmt, Connection conn) { if (rs ! null) try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } if (stmt ! null) try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } if (conn ! null) try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } // 注意此处 close() 是释放连接回池非物理关闭 } }为什么选手写 Dbcp 而非直接DriverManager.getConnection()性能DriverManager每次创建新连接耗时 100ms而连接池复用连接1ms资源控制setMaxActive(20)防止高并发时 Oracle 连接数爆满Oracle 11g Express Edition 默认仅 20 连接课程设计意图体现“数据库连接管理”知识点比裸写DriverManager更贴近工程实践。3.3 分页查询的 Oracle 特殊语法ROWNUM的陷阱与正确写法PDF 第 4.2.2 节queryFlightdata()方法中SQL 使用了?占位符但未给出Constants.QUERY_FLIGHTBASICINFO的具体内容。根据stmt.setInt(1, curPage*rowsPrePage)和stmt.setInt(2,(curPage-1)*rowsPrePage1)可反推其为 Oracle 分页 SQL-- 错误写法常见翻车点直接用 ROWNUM 限制范围 SELECT * FROM flight WHERE ROWNUM BETWEEN ? AND ?; -- 问题ROWNUM 在 WHERE 执行前分配BETWEEN 10 AND 20 会返回空因 ROWNUM 从1开始 -- 正确写法PDF 实际采用子查询嵌套 SELECT * FROM ( SELECT a.*, ROWNUM rnum FROM ( SELECT * FROM flight ORDER BY flightnum ) a WHERE ROWNUM ? ) WHERE rnum ?; -- 参数1上界curPage*rowsPrePage参数2下界(curPage-1)*rowsPrePage1参数说明?占位符顺序必须与setInt()一致第一个?对应curPage*rowsPrePage如第2页每页10条则为20第二个?对应(curPage-1)*rowsPrePage1如为11ORDER BY flightnum必须存在否则ROWNUM分配无序分页结果错乱若用 MySQL可直接LIMIT ?,?但 Oracle 必须用ROWNUM嵌套——这是课程设计刻意考察的数据库方言差异。4. Swing 界面与业务逻辑的耦合订票时余票校验的 4 层防御机制4.1 界面-逻辑分离架构Action→Dao→Database的三层调用链PDF 第 4.2.1 节描述了整体流程“主界面功能选择 → Action 模块 → Dao 包 → 数据库”。以订票为例调用链为Swing 界面层handin()方法PDF 第 4.2.4 节获取用户输入姓名、身份证、航班号、票数Action 层校验输入长度len1len2...、构造flightVo对象Dao 层调用flightdao.queryflightinfo3(flightnum)查询当前余票再调用addFlightinfo()插入订票记录Database 层SQL 执行INSERT INTO customer ...并更新flight.ticket。注意flightVo是 Value Object封装数据flightdao是 Data Access Object封装数据库操作这种分层虽简陋无 Service 层但已体现 MVC 思想雏形。4.2 余票校验的血泪经验从“查余票”到“锁余票”的 4 层防御PDF 第 4.2.4 节handin()方法展示了完整的订票逻辑其核心是余票校验。但原文代码存在并发风险多个用户同时订同一航班实际复现时需补足// 改进版 handin() 关键逻辑加锁事务 private void handin() { String flightnum o.getJbtflight().getText().trim(); int ticketReq Integer.parseInt(o.getJbtadultticketnumber().getText()); // 1. 查询余票SELECT FOR UPDATE 锁行 int available flightdao.queryAvailableTickets(flightnum); // SQL: SELECT ticket FROM flight WHERE flightnum? FOR UPDATE // 2. 业务校验余票是否充足 if (ticketReq available) { // 3. 更新余票原子操作 flightdao.updateFlightTicket(flightnum, available - ticketReq); // UPDATE flight SET ticket? WHERE flightnum? // 4. 插入订票记录事务内 flightdao.addFlightinfo(vo); flightdao.addFlightinfo1(vo); JOptionPane.showMessageDialog(dialog, 订票成功); } else { // 候补逻辑同原文 int waitCount ticketReq - available; vo.setCustomtype(已候补); vo.setTick(waitCount); flightdao.addFlightinfo2(vo); JOptionPane.showMessageDialog(dialog, 余票不足 waitCount 张加入候补队列); } }4 层防御说明第1层锁SELECT ... FOR UPDATE防止并发读取同一航班余票否则 A/B 同时查到余票10各订5张最终余票-0第2层校验ticketReq available判断逻辑原文已实现第3层原子更新UPDATE flight SET ticket ticket - ?确保余票扣减不可分割第4层事务整个订票过程查、更、插需包裹在Connection.setAutoCommit(false)commit()中否则部分失败导致数据不一致。4.3 候补队列的自动触发退票后如何唤醒候补用户PDF 第 4.2.4 节jbOK()退票方法中有flightdao.flightquery1(id1)查询用户订票数但未实现“退票后检查候补队列并升舱”。需补充逻辑// 退票后检查候补伪代码 if (flightdao.deleteFlightinfo1(vo) 0) { // 删除候补记录 // 查询候补队列中相同航班的用户 ListflightVo waitList flightdao.queryWaitListByFlight(flightnum); if (!waitList.isEmpty()) { // 取队首用户升为正式订票 flightVo waitUser waitList.get(0); waitUser.setCustomtype(已定票); flightdao.addFlightinfo(waitUser); // 插入customer表 flightdao.deleteFlightinfo2(waitUser); // 删除候补记录 JOptionPane.showMessageDialog(dialog, 候补用户已升舱); } }关键点候补用户存于customer表C_type已候补退票后需SELECT * FROM customer WHERE flightnum? AND C_type已候补 ORDER BY id按ID排序模拟FIFO取第一条执行升舱。5. 避坑指南复现这个课程设计时90% 的人卡在这 5 个玄学问题5.1 现象PowerDesigner 生成的建表 SQL 在 Oracle 中执行报错ORA-00907: missing right parenthesis原因PowerDesigner 默认生成CREATE TABLE flight (flightnum VARCHAR2(20) PRIMARY KEY, ...)但 Oracle 11g 要求主键约束必须显式命名或使用CONSTRAINT pk_flight PRIMARY KEY (flightnum)语法。解决手动修改 DDL在PRIMARY KEY前加CONSTRAINT pk_flight或直接删掉PRIMARY KEY在表末尾加CONSTRAINT pk_flight PRIMARY KEY (flightnum)。5.2 现象Eclipse 运行时抛出java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver原因ojdbc6.jar未正确添加到 Build Path或 JDK 版本与驱动不匹配如用 ojdbc8.jar 配 JDK 1.6。解决下载ojdbc6.jarOracle 官网搜索 “Oracle Database 11g Release 2 JDBC Drivers”右键项目 →Properties→Java Build Path→Libraries→Add External JARs→ 选择ojdbc6.jar确认 Eclipse 使用的 JRE 是 JDK 1.6/1.7Preferences→Java→Installed JREs。5.3 现象Swing 界面表格tModel.setDataVector()后不刷新显示空白原因Swing 组件刷新需revalidate()重新验证布局 repaint()重绘PDF 中只写了table.revalidate()漏了table.repaint()。解决在setDataVector()后添加table.repaint()或直接调用table.updateUI()。5.4 现象Oracle 查询SELECT * FROM flight返回中文字段名乱码如????原因Oracle 客户端字符集NLS_LANG与数据库字符集不一致常见于 Windows 系统默认AMERICAN_AMERICA.WE8MSWIN1252而数据库为AL32UTF8。解决在 Windows 环境变量中新增NLS_LANGAMERICAN_AMERICA.AL32UTF8或在 Eclipse 运行配置中VM arguments添加-Dfile.encodingUTF-8重启 Eclipse 生效。5.5 现象订票成功后flight.ticket余票未减少仍为原值原因DAO 层updateFlightTicket()方法中 SQL 写成UPDATE flight SET ticket ? WHERE flightnum ?但传入的参数是available - ticketReq新余票值而原文queryflightinfo3()返回的是旧余票值若未在事务中执行可能被其他线程覆盖。解决改用UPDATE flight SET ticket ticket - ? WHERE flightnum ?直接减避免读-改-写竞态并确保该 SQL 与插入customer记录在同一事务中。6. 进阶技巧用 Oracle 11g 物化视图加速航班满座率与财务统计6.1 满座率计算的性能瓶颈实时 JOIN 的代价PDF 第 2.1 节需求要求“计算航班满座率”即已订票数 / 总座位数。但customer表只存订票记录flight表无total_seats字段只有ticket余票。原文未定义总座位数需假设flight.total_seats字段存在或从airfirm表推算。实时计算需JOIN customer和flight当customer表达百万级时SELECT f.flightnum, COUNT(c.id)/f.total_seats FROM flight f LEFT JOIN customer c ON f.flightnumc.flightnum GROUP BY f.flightnum极慢。6.2 物化视图Materialized View方案预计算 自动刷新Oracle 11g 支持物化视图可将满座率结果固化为表并设为ON COMMIT刷新每次订票/退票事务提交后自动更新-- 创建物化视图日志必需 CREATE MATERIALIZED VIEW LOG ON flight WITH PRIMARY KEY, ROWID; CREATE MATERIALIZED VIEW LOG ON customer WITH PRIMARY KEY, ROWID; -- 创建物化视图航班满座率 CREATE MATERIALIZED VIEW mv_flight_occupancy BUILD IMMEDIATE REFRESH ON COMMIT AS SELECT f.flightnum, f.startplace, f.endplace, NVL(COUNT(c.id), 0) AS booked_count, f.total_seats, ROUND(NVL(COUNT(c.id), 0) / NULLIF(f.total_seats, 0), 4) AS occupancy_rate FROM flight f LEFT JOIN customer c ON f.flightnum c.flightnum AND c.C_type 已定票 GROUP BY f.flightnum, f.startplace, f.endplace, f.total_seats;参数说明BUILD IMMEDIATE立即构建非延迟REFRESH ON COMMIT事务提交时刷新保证数据强一致NVL(COUNT(c.id), 0)处理无订票航班COUNT 返回 NULLNVL 转为 0NULLIF(f.total_seats, 0)防除零错误total_seats 为 0 时返回 NULLROUND 处理为 NULL。6.3 财务统计的增量聚合用物化视图替代SUM(income)-SUM(outcome)PDF 第 4.2.5 节财务查询需“每周/每月营业收入”即SUM(income) - SUM(outcome)。但airfirm表的income/outcome字段是累计值无法直接按时间聚合。更优方案是新增finance_log表记录每次订票income、退票-income、支出-outcome的明细创建物化视图按TRUNC(log_time, MM)月或TRUNC(log_time, WW)周分组聚合-- finance_log 表结构 CREATE TABLE finance_log ( id NUMBER PRIMARY KEY, flightnum VARCHAR2(20), amount FLOAT, log_type VARCHAR2(10), -- INCOME, OUTCOME log_time DATE DEFAULT SYSDATE ); -- 按月财务统计物化视图 CREATE MATERIALIZED VIEW mv_monthly_finance BUILD IMMEDIATE REFRESH ON COMMIT AS SELECT TRUNC(log_time, MM) AS month_start, SUM(CASE WHEN log_typeINCOME THEN amount ELSE 0 END) AS total_income, SUM(CASE WHEN log_typeOUTCOME THEN amount ELSE 0 END) AS total_outcome, SUM(CASE WHEN log_typeINCOME THEN amount ELSE 0 END) - SUM(CASE WHEN log_typeOUTCOME THEN amount ELSE 0 END) AS net_profit FROM finance_log GROUP BY TRUNC(log_time, MM);落地效果查询本月利润只需SELECT * FROM mv_monthly_finance WHERE month_startTRUNC(SYSDATE, MM)毫秒级响应无需扫全表。从那以后我每次做课程设计数据库项目只要涉及高频聚合查询满座率、营收统计都会强制走一遍物化视图方案——不是为了炫技而是让学生亲眼看到数据库优化不是调参数而是用对的工具解决对的问题。这份 PDF 里的订票系统表面是 Swing 界面和 JDBC内核却是 Oracle 11g 的企业级能力。希望帮到你。本文还有配套的精品资源点击获取