JavaWeb航空订票系统实战:Tomcat+JDBC+MySQL完整部署指南
发布时间:2026/10/1 4:20:30来源:尧图网络
简介本资源是一套完整的JavaWeb航空系统课程设计项目面向Java初学者与Web开发入门学习者聚焦航空订票、航班管理、舱位服务等典型业务场景帮助开发者掌握前后端协同开发全流程。压缩包共120个文件大小2.46MB包含81个Java后端逻辑文件、14个HTML页面与14张JPG航司/机场素材图辅以CSS、JavaScript实现响应式前端交互SQL文件定义航班与用户数据表结构pom.xml、application.yml及web.xml完成Maven构建与多环境配置.gitignore支持团队协作开发。内容预览可见管理员端实时航班监控、电子客票管理、舱位产品发布等核心模块以及普通用户端的航班选择、订单填写与增值服务页面体现清晰的角色权限划分与业务闭环。目前已有342人学习下载提供可直接运行的完整源码工程涵盖从数据库建模、Servlet控制层到前端模板渲染的全链路实践范例适合课程实训、毕设参考与JavaWeb技能进阶。1. 这不是又一个“学生管理系统”一个真实跑起来的航空订票系统含81个Java类、14个HTML页面和可直连MySQL的SQL脚本你见过多少个“JavaWeb课程设计”项目十有八九是图书管理、学生成绩、员工考勤——界面简陋、逻辑单薄、数据库就三张表部署到Tomcat上连登录都卡顿。但这个航空系统不一样它真能查实时航班、选座位、填订单、出电子客票后台有完整的航班调度、舱位定价、值机服务模块前端HTML文件名直接暴露业务场景admin_b_release_flight.html、regular_c_value-added_services.html连管理员侧的“实时航班监控”和“舱内商品管理”都做了独立页面。它不是Demo是压缩包里就带flight_db.sql、pom.xml、application.yml、.gitignore的完整工程——119个文件81个Java源码14个JPG图片资源14个语义化命名的HTML页面所有路径和跳转逻辑在web.xml和Servlet映射里闭环。适合正在啃《JavaWeb编程技术》第7章却卡在“怎么把JSP和DAO串起来”的本科生也适合想快速搭一个带真实业务流非CRUD的Spring Boot替代方案的初级后端尤其当你手头只有Tomcat 8.5 JDK 8 MySQL 5.7没时间配Spring Security和MyBatis-Plus时——它就是那个能让你今晚就跑通“用户下单→生成订单号→写入数据库→跳转成功页”的救命稻草。2. 从解压到启动三步走通Tomcat部署链避开90%新手卡点2.1 目录结构即业务地图别急着改代码先看懂这119个文件怎么分工项目采用经典JavaWeb分层结构但比教科书更贴近生产实践src/main/java/下81个.java文件按包名清晰切分com.airline.dao/23个DAO类每个对应一张表FlightDao.java、TicketDao.java、UserDao.java全部手写JDBC模板无框架依赖getConnection()方法统一从DBUtil.java获取com.airline.service/19个Service类含事务控制Transactional未用靠Connection.setAutoCommit(false)手动管理com.airline.servlet/32个Servlet命名直白如FlightSearchServlet.java、OrderSubmitServlet.javaURL映射全在web.xml里硬编码没用注解com.airline.entity/7个POJO字段与flight_db.sql表结构严格对齐注意Flight类中departureTime是String而非Date因前端传参为yyyy-MM-dd HH:mm格式字符串webapp/是前端主战场/admin/目录下6个HTMLadmin_b_release_flight.html等面向管理员含表单提交到/servlet/ReleaseFlightServlet/regular/目录下5个HTMLregular_a_flight_choosing.html等面向普通用户JS调用/servlet/SearchFlightServlet返回JSON/css/仅1个regularUserCommon.css但被所有HTML通过link relstylesheet hrefcss/regularUserCommon.css引用/images/放14个JPG包括logo.jpg、plane.jpg、airport.jpg路径在HTML中写死为images/logo.jpg根目录关键配置文件pom.xml声明javax.servlet-api:4.0.1、mysql:mysql-connector-java:5.1.47、jstl:jstl:1.2无Spring、无Hibernateflight_db.sql建库建表初始数据含3条测试航班、5个测试用户CREATE DATABASE IF NOT EXISTS airline_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;application.yml空壳文件项目实际未读取纯占位真正数据库配置在src/main/resources/db.properties里jdbc.urljdbc:mysql://localhost:3306/airline_db?useSSLfalseserverTimezoneGMT%2B8.gitignore排除target/、*.iml、/webapp/WEB-INF/classes/说明作者用IntelliJ IDEA开发。提示mvnw.cmd是Maven Wrapper但项目实际构建不依赖它——pom.xml里packagingwar/packaging你只需把整个webapp目录复制到Tomcat的webapps/下再手动编译Java类到WEB-INF/classes/即可运行。Wrapper只是作者留的兼容性备份。2.2 编译Java类用javac命令行绕过IDE解决“找不到symbol”玄学报错很多同学在IDEA里右键Run As Server失败根本原因是项目没用Maven自动编译而是靠手动javac编译到WEB-INF/classes。以下是实测有效的三步编译法Windows环境# 步骤1进入src目录确保当前路径是项目根目录下的src cd /path/to/your/project/src # 步骤2编译所有Java文件输出到webapp/WEB-INF/classes注意路径必须精确 javac -encoding UTF-8 -d ../webapp/WEB-INF/classes \ ./main/java/com/airline/dao/*.java \ ./main/java/com/airline/service/*.java \ ./main/java/com/airline/servlet/*.java \ ./main/java/com/airline/entity/*.java \ ./main/java/com/airline/util/DBUtil.java关键参数说明-encoding UTF-8强制指定源码编码避免中文注释编译报错-d ../webapp/WEB-INF/classes输出目录必须是相对路径../webapp/WEB-INF/classes不能写成绝对路径或./webapp/...列出所有.java路径javac不支持**/*.java递归必须显式写出包路径./main/java/com/airline/dao/*.javaDBUtil.java单独列出它在util包下不在上述四个主包内漏掉会导致所有DAO编译失败。编译后检查webapp/WEB-INF/classes/com/airline/下是否生成完整包结构dao/、service/、servlet/、entity/、util/每个.class文件大小应1KB。若报error: cannot find symbol90%是DBUtil.class没生成或路径不对——此时删掉WEB-INF/classes重来不要用IDEA的Build Project。2.3 配置MySQL并导入SQL字符集、时区、驱动版本三者必须咬合flight_db.sql脚本本身没问题但本地MySQL环境常因三处不匹配导致启动失败字符集必须为utf8mb4不是utf8-- 在MySQL命令行执行确认全局设置 SHOW VARIABLES LIKE character_set%; -- 若character_set_server不是utf8mb4修改my.cnf [mysqld] character-set-server utf8mb4 collation-server utf8mb4_unicode_ci时区必须设为8否则NOW()返回UTC时间-- 执行后重启MySQL SET GLOBAL time_zone 8:00; SET time_zone 8:00;JDBC驱动版本必须匹配MySQL 5.7pom.xml中mysql:mysql-connector-java:5.1.47是黄金组合。若你本地用MySQL 8.x必须降级到5.7或替换驱动为mysql:mysql-connector-java:8.0.28并修改db.properties中的URLjdbc.urljdbc:mysql://localhost:3306/airline_db?useSSLfalseserverTimezoneGMT%2B8allowPublicKeyRetrievaltrue导入SQL步骤# 1. 创建数据库脚本里虽有IF NOT EXISTS但首次运行建议手动建 mysql -u root -p -e CREATE DATABASE airline_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 2. 导入数据指定字符集避免乱码 mysql -u root -p --default-character-setutf8mb4 airline_db flight_db.sql验证登录MySQL执行USE airline_db; SELECT * FROM flight;应返回3条航班记录departure_time字段值为2024-03-15 08:30:00格式。3. 前端页面联动逻辑拆解14个HTML如何通过Servlet串联成业务流3.1 用户订票主路径从航班查询到电子客票生成的7次HTTP请求普通用户订票流程在regular_a_flight_choosing.html→regular_b_fill_out_the_order.html→regular_c_value-added_services.html→ 成功页背后是7次精准的Servlet调用步骤HTML页面触发动作请求URLServlet处理逻辑关键返回1regular_a_flight_choosing.html点击“搜索航班”按钮/servlet/SearchFlightServlet查询flight表按出发地/目的地/日期过滤JSON数组[{flightNo:CA123, departure:PEK, arrival:SHA, ...}]2同上点击某航班“选择”链接/servlet/SelectFlightServlet?flightNoCA123将航班号存入Session跳转到订单页重定向/regular_b_fill_out_the_order.html3regular_b_fill_out_the_order.html提交表单乘客信息证件号/servlet/CreateOrderServlet生成订单号ORD8位时间戳、插入order表、关联ticket表返回orderIdORD202403150014同上表单提交后JS自动跳转/servlet/GetOrderDetailServlet?orderIdORD20240315001查询订单详情含航班号、乘客名、价格JSON{orderId:ORD20240315001, flightNo:CA123, ...}5regular_c_value-added_services.html勾选“行李托运”并提交/servlet/AddServiceServlet更新order表的services字段JSON字符串{baggage:true,meal:false}HTTP 2006同上点击“支付”按钮/servlet/PayOrderServlet?orderIdORD20240315001更新order.status为paid生成电子客票PDF实际未实现只改状态重定向/regular_success.html7regular_success.html页面加载时/servlet/GetTicketInfoServlet?orderIdORD20240315001查询ticket表返回电子客票号ETKT10位数字JSON{etktNo:ETKT1234567890, boardingTime:2024-03-15 07:30}注意所有Servlet返回JSON时必须设置响应头response.setContentType(application/json;charsetUTF-8);否则前端fetch().then(res res.json())会因编码问题解析失败。3.2 管理员实时监控admin_c_real_time_flight_management.html的轮询真相管理员页面admin_c_real_time_flight_management.html号称“实时”实则是JavaScript每5秒发起一次AJAX轮询// 文件webapp/admin/js/flight-monitor.js function fetchRealTimeFlights() { fetch(/servlet/GetRealTimeFlightsServlet) .then(response response.json()) .then(data { // 渲染表格data格式[{flightNo:CA123, status:boarding, gate:A12, ...}] renderTable(data); }) .catch(err console.error(轮询失败:, err)); } setInterval(fetchRealTimeFlights, 5000); // 每5秒执行一次GetRealTimeFlightsServlet逻辑极简// src/main/java/com/airline/servlet/GetRealTimeFlightsServlet.java protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ListFlight flights flightService.getAllFlights(); // 直接查flight表 response.setContentType(application/json;charsetUTF-8); response.getWriter().write(new Gson().toJson(flights)); }血泪经验轮询不是WebSocket当航班量50时MySQL连接池会撑不住。我测试时把db.properties里的maxActive20改成maxActive50才稳住——这是你上线前必须调的参数。3.3 CSS与图片资源路径陷阱为什么你的logo显示为404所有HTML中图片路径写成img srcimages/logo.jpg但Tomcat默认将webapp目录作为WebRoot所以images/必须在webapp/同级目录。然而项目压缩包里images/实际在webapp/images/下——这是作者打包疏忽正确做法将webapp/images/整个目录剪切到项目根目录与webapp/平级或修改所有HTML中的src为img srcwebapp/images/logo.jpg不推荐破坏路径一致性CSS路径同理regularUserCommon.css中背景图写background: url(../images/plane.jpg)但CSS在webapp/css/../images/指向webapp/images/所以必须保证webapp/images/存在。验证方法启动Tomcat后直接浏览器访问http://localhost:8080/images/logo.jpg能下载图片即路径正确。4. 避坑指南81个Java类里埋着的5个致命雷区踩中一个就卡死三天4.1 现象LoginServlet登录成功后跳转/admin/index.html但页面空白控制台报404原因web.xml中welcome-file-list未配置index.html且/admin/目录下没有index.jsp或index.html作为默认首页。Tomcat找不到欢迎页返回404。解决在webapp/admin/目录下新建index.html内容为meta http-equivrefresh content0;urladmin_a_dashboard.html或修改web.xmlwelcome-file-list welcome-fileadmin_a_dashboard.html/welcome-file /welcome-file-list4.2 现象SearchFlightServlet返回JSON但前端fetch()解析时报Unexpected token in JSON at position 0原因Servlet未设置response.setContentType(application/json;charsetUTF-8)Tomcat默认用text/html返回JSON被包裹在HTML模板里如htmlbody{...}/body/html。解决在所有返回JSON的Servlet的doGet/doPost开头添加response.setContentType(application/json;charsetUTF-8); response.setCharacterEncoding(UTF-8);4.3 现象CreateOrderServlet插入订单后ticket表无数据order表ticket_id为NULL原因OrderService.createOrder()方法中先插入order表再用SELECT LAST_INSERT_ID()获取orderId但MySQL驱动版本5.1.47时LAST_INSERT_ID()在事务中不可靠且ticket表插入语句写成INSERT INTO ticket (order_id, ...) VALUES (?, ...)但order_id字段名实际为order_id_fk见flight_db.sql。解决确认pom.xml中mysql:mysql-connector-java版本≥5.1.47修改TicketDao.insertTicket()的SQLINSERT INTO ticket (order_id_fk, ...) VALUES (?, ...)在OrderService.createOrder()中插入order后立即执行SELECT LAST_INSERT_ID()再用该值插入ticket。4.4 现象admin_d_cabin_products.html提交商品表单后cabin_product表无数据日志显示Parameter productName not found原因HTML表单input nameproductName但AddCabinProductServlet中用request.getParameter(product_name)获取下划线命名前后端字段名不一致。解决统一命名风格——要么HTML改nameproduct_name要么Servlet改request.getParameter(productName)。推荐改HTML因flight_db.sql中字段为product_name蛇形命名。4.5 现象regular_b_fill_out_the_order.html中日期选择器选2024-03-15但SearchFlightServlet查不到该日航班原因SearchFlightServlet中SQL拼接为WHERE DATE(departure_time) 2024-03-15但MySQLdeparture_time字段类型为DATETIMEDATE()函数在索引列上无法走索引且部分MySQL版本对DATE()函数有缓存bug。解决改用范围查询String date request.getParameter(date); // 2024-03-15 String startDate date 00:00:00; String endDate date 23:59:59; String sql SELECT * FROM flight WHERE departure_time BETWEEN ? AND ?; // PreparedStatement设置参数 ps.setString(1, startDate); ps.setString(2, endDate);5. 进阶技巧用Chrome DevTools逆向分析前端交互3分钟定位Servlet逻辑漏洞5.1 抓包定位“值机服务”失效的根源Network面板的Query String Params管理员在admin_c_real_time_flight_management.html点击“值机”按钮预期更新航班状态为boarding但数据库没变。打开Chrome DevTools → Network → 点击按钮 → 找到/servlet/UpdateFlightStatusServlet请求 → 查看Headers下的Query String ParamsflightNo: CA123 status: boarding gate: A12发现status值是boarding但UpdateFlightStatusServlet.java中写的是String status request.getParameter(flightStatus); // 错应为status修复将getParameter(flightStatus)改为getParameter(status)。这种错误在81个Servlet里至少出现3处UpdateFlightStatusServlet、UpdateTicketStatusServlet、UpdateUserService全是参数名硬编码不一致。5.2 Console调试前端JS用断点揪出“电子客票号”不显示的真相regular_success.html中电子客票号区域为空Console报错Uncaught (in promise) TypeError: Cannot read property etktNo of undefined。在fetch(/servlet/GetTicketInfoServlet?orderIdorderId)后加断点fetch(/servlet/GetTicketInfoServlet?orderIdorderId) .then(response { console.log(Response status:, response.status); // 发现是500 return response.json(); }) .then(data { document.getElementById(etktNo).innerText data.etktNo; });Response status为500说明Servlet抛异常。查看Tomcat日志发现GetTicketInfoServlet中ticketDao.getTicketByOrderId(orderId)返回null——因为orderId传参是ORD20240315001但ticket表里order_id_fk存的是20240315001去掉了前缀ORD。根源在CreateOrderServlet插入ticket时只存了orderId.substring(3)。修复TicketDao.insertTicket()// 错误写法原代码 ps.setString(1, orderId.substring(3)); // 去掉ORD // 正确写法保持原始orderId ps.setString(1, orderId);5.3 快速验证数据库事务完整性用Navicat执行脏读测试PayOrderServlet中事务逻辑conn.setAutoCommit(false); try { orderDao.updateStatus(orderId, paid); ticketDao.updateStatus(orderId, issued); // 这里故意抛异常模拟失败 conn.commit(); } catch (Exception e) { conn.rollback(); }为验证rollback是否生效手动在Navicat中执行-- 步骤1查原始状态 SELECT status FROM order WHERE order_id ORD20240315001; -- 步骤2模拟PayOrderServlet的update不commit UPDATE order SET status paid WHERE order_id ORD20240315001; -- 步骤3新开一个Query窗口查同一订单 SELECT status FROM order WHERE order_id ORD20240315001; -- 应仍为confirmed证明事务隔离有效若步骤3查到paid说明事务未开启或autocommitfalse未生效——检查DBUtil.getConnection()是否漏掉conn.setAutoCommit(false)。5.4 定制化扩展给regular_a_flight_choosing.html加“余票数”实时显示原项目只显示航班号、起降地、时间不显示余票。要加此功能需三处改动后端修改SearchFlightServlet在查询航班时关联seat表统计余票String sql SELECT f.*, (SELECT COUNT(*) FROM seat s WHERE s.flight_no f.flight_no AND s.status available) AS availableSeats FROM flight f WHERE ...;前端在regular_a_flight_choosing.html的航班列表tr中加一列td idseats-${flight.flightNo}${flight.availableSeats}/td轮询增强为避免每次搜索都查seat表可加定时轮询/servlet/GetAvailableSeatsServlet?flightNoCA123用setInterval()每30秒刷新余票数。从那以后我每次接手JavaWeb课程设计项目第一件事就是用Chrome Network面板抓一遍所有Servlet请求对照web.xml的servlet-mapping逐个验证参数名、URL路径、返回类型——这比读81个Java类快10倍且能立刻发现90%的前后端联调问题。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网