ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于Spring Boot + MyBatis的机票预订系统项目实战解析

基于Spring Boot + MyBatis的机票预订系统项目实战解析 简介这是一套面向Java初学者与高校数据库课程设计学生的实战型机票预订系统项目聚焦Java桌面应用开发与MySQL数据库协同实践完整覆盖用户注册登录、航班查询、座位选择、订单生成等核心业务流程。资源包共37个文件含7个Java源码文件如Care.java、Ticket.java等体现MVC分层逻辑、7个编译后class文件、15个XML配置文件支撑界面布局与数据绑定、1份Word版项目说明书及1个SQL建表脚本整体压缩后仅208KB轻量易部署。已有8302人学习下载适合用于课程设计答辩、期末实训或JavaMySQL综合能力提升。读者可直接运行调试深入理解JDBC连接机制、GUI界面交互逻辑、第三范式数据库设计及常见并发订座控制策略配套文档还详细说明了各模块职责与测试要点。 最近总有刚学完 Java 基础的朋友问我说想找一个能写进简历、又不用太大工程量的练手项目。我基本回答都是同一个做一个基于 Java MySQL 的机票预订系统。为什么是它因为一个成熟的机票预订系统功能足够清晰流程足够完整又不会像电商秒杀那样把难度直接拉满。注册登录、航班查询、下单购票、订单管理、后台管理这些链路几乎覆盖了 JavaWeb 开发里最常见的一整套知识点。这篇文章我就用 Spring Boot MyBatis MySQL 这套组合把整个项目的设计思路、数据库建模、后端实现、前端页面、部署步骤和踩坑记录完整走一遍。不管你是准备课程设计还是想给简历加一个项目都可以直接对照着做。我把每一步为什么这样做也讲清楚方便你面试的时候能讲明白而不是简历写了项目、一问细节就露馅。1. 项目整体设计与技术选型思路1.1 为什么我推荐拿机票预订系统练手先说说为什么选机票预订系统而不是图书管理系统、员工管理系统这类烂大街的项目。图书管理这类系统最大的问题是业务逻辑太薄本质就是单表增删改查做完之后对“业务设计”几乎没有感知。面试官问一句“你这个系统有什么难点”你大概率只能回答“CRUD 比较多”没有任何亮点。机票预订系统不一样它涉及库存、状态流转、乘客实名信息、并发购票这些真实业务点。你亲手写一次库存扣减和订单状态管理再去看别的业务系统会发现很多东西是相通的。比如下单时余票不够怎么办、两个人同时买最后一张票怎么办、订单支付超时要不要处理这些问题都是可以在简历上写的真实业务难点。项目含金量一下就上来了。还有一个很现实的原因机票预订系统的数据关系非常典型用户、订单、航班三张表之间是多对多、一对多的关系练的就是关联查询和事务处理。这种表关联设计在真实项目中到处都是你把这个项目吃透了以后做其他管理系统的数据库设计基本都能顺手很多。1.2 技术栈怎么定Spring Boot MyBatis MySQL这次项目我用的组合是 JDK 8 Spring Boot 2.7 MyBatis MySQL 5.7 Bootstrap JSP Maven。先解释一下这套选型免得你只照着敲代码却不知道为什么是这个组合。技术版本建议选型理由JDK1.8稳定、生态资料最多大部分公司还在用Spring Boot2.7.x内置 Tomcat配置少适合快速开发MyBatis3.xSQL 完全可控面试高频跟 MySQL 配合好MySQL5.7 或 8.0经典稳定社区资料多Navicat 可视化方便前端JSP Bootstrap不用额外搭 Node 环境适合快速交付Maven3.6依赖管理和项目打包行业标配你可能会问为什么不用现在很火的 Vue 做前后端分离我的理由是对于第一次做项目实战的人来说前后端分离要同时处理跨域、Token 认证、接口文档、前端构建这些额外问题这些内容会大幅冲淡你对核心业务逻辑的关注。用 JSP 页面加 Controller 直出你可以把精力集中在后端的业务代码和 SQL 上。项目跑通了再按我文末的方案改造成前后端分离反而事半功倍。至于 Spring Boot 而不是 SSM 手动配置理由更直接。Spring Boot 把大量繁琐的 XML 和 Bean 配置自动化了你可以用很少的代码把项目跑起来这对新手特别友好。而且现在中小厂后端基本也都是 Spring Boot学了不亏。MyBatis 则是让你保留对 SQL 的完全控制像库存扣减这种需要精确 SQL 的操作用 MyBatis 写起来比 JPA 更直观。MySQL 选 5.7 已经够用如果你装了 8.0 也没关系后面第 5 章我会专门讲版本差异带来的坑。1.3 项目功能模块与包结构划分功能上我把它拆成两个端前台用户端和后台管理端。用户端有注册登录、航班查询、下单购票、个人订单管理管理端有航班管理、订单管理、出票退票处理。听起来模块不多但每个模块都有值得讲的细节。比如航班查询通常要支持“出发城市、到达城市、出发日期”三个条件日期为空时要能查全部订单模块要有状态字段不同状态下显示不同操作按钮这些细节才是真实项目的样子。代码结构我按常见的分层来建方便你以后扩展。controller 层负责接收请求和参数校验service 层负责业务逻辑比如下单时库存扣减和订单生成必须在一个事务里mapper 层负责数据库操作entity 就是实体类。我直接把包结构给你照着建就行com.airline ├── controller // 控制层UserController、FlightController、OrderController ├── service // 业务层UserService、FlightService、OrderService ├── mapper // 数据访问层UserMapper、FlightMapper、OrderMapper ├── entity // 实体类User、Flight、Order ├── interceptor // 拦截器LoginInterceptor ├── common // 通用类Result 返回结果封装 └── config // 配置类WebConfig 注册拦截器每层之间单向依赖controller 调 serviceservice 调 mapper不要越层调用。我刚带项目时见过有人直接在 controller 里写 JDBC代码全揉在一起后期想改业务逻辑牵一发动全身。分层清晰是项目可维护性的第一步。2. 数据库设计先想清楚表再动手写代码2.1 核心表结构拆解用户、航班、订单数据库设计是这类项目里最不该跳过的部分。很多新手一上来就写代码写到关联查询才发现表结构不合理返工特别痛苦。我建议你先把表想清楚再动手建表顺序也固定先建用户表再建航班表最后建订单表。用户表存的是登录账号和乘客信息。用户名和密码是必须的真实姓名、手机号、身份证号属于购票实名信息虽然可以直接让用户在下单时填但提前存在用户表里会让用户下单更顺畅也是实际航空系统的做法。航班表是核心业务表一条记录就是一个航班。航班号、航空公司、出发城市、到达城市、出发时间、到达时间、票价、余票数都是重点。这里余票数就是库存是整个系统最需要小心处理的字段。订单表把用户和航班关联起来同时记录乘客信息和成交价格。订单表是业务真正的核心查询最频繁设计要尤其注意冗余字段具体原因我在 2.2 节详细说。2.2 字段类型与约束为什么这样选先看用户表。用户名要加唯一约束这是登录查询的依据否则注册时可能出现重复账号。密码字段长度给到 255不要只给 32因为后面可能升级加密算法MD5 是 32 位BCrypt 是 60 位留足余量是习惯问题。角色字段 role 用 tinyint 表示0 是管理员1 是普通用户比用字符串更省空间判断也方便。再看航班表。价格字段必须用 decimal(10,2)千万别用 float 或 double。二进制浮点数在计算金额时有精度误差哪怕账面上只差 0.01累积起来也是麻烦事。余票字段 stock 用 int默认值 0后面扣减库存时要用它做条件判断。航班号要加唯一索引因为这是业务上天然的唯一标识。订单表的设计是这套系统里含金量最高的地方。我把乘客姓名和身份证号直接冗余在订单表里不通过 user_id 去用户表反查原因是机票支持帮别人买乘客不一定是注册用户。既然订单要求实名那就必须把乘客信息快照到订单里。类似地订单里还冗余了 price记录购买时的成交价因为航班价格会变订单表要保留历史快照。这个“冗余快照”的思路面试官非常爱问你答出来就是加分项。订单状态 status 用 tinyint0 待支付、1 已支付、2 已出票、3 已取消、4 已退票。不要直接存中文不好扩展也不好比较。实际开发中一般会在代码里用常量类或者枚举统一管理这些状态JSP 页面再根据状态值显示对应按钮。另外三张表之间我故意没用数据库外键约束只保留逻辑关联字段 user_id、flight_id。外键虽然能保证数据一致性但会带来额外的锁开销和耦合线上项目普遍不用外键靠业务代码控制。这个点你也可以在面试时说显得你了解实际工程做法。2.3 建表 SQL 与初始化数据下面是完整的建表 SQL我按实际项目标准写好注释你直接用 Navicat 或命令行执行即可CREATE DATABASE IF NOT EXISTS airline DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE airline; -- 用户表 CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT COMMENT 主键, username varchar(50) NOT NULL COMMENT 用户名, password varchar(255) NOT NULL COMMENT 密码演示用MD5加密, real_name varchar(50) DEFAULT NULL COMMENT 真实姓名, phone varchar(20) DEFAULT NULL COMMENT 手机号, id_card varchar(18) DEFAULT NULL COMMENT 身份证号, role tinyint NOT NULL DEFAULT 1 COMMENT 角色0管理员1普通用户, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 注册时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; -- 航班表 CREATE TABLE flight ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, flight_no varchar(20) NOT NULL COMMENT 航班号, airline varchar(50) DEFAULT NULL COMMENT 航空公司, departure_city varchar(50) NOT NULL COMMENT 出发城市, arrival_city varchar(50) NOT NULL COMMENT 到达城市, departure_time datetime NOT NULL COMMENT 起飞时间, arrival_time datetime NOT NULL COMMENT 到达时间, price decimal(10,2) NOT NULL COMMENT 票价, stock int NOT NULL DEFAULT 0 COMMENT 余票数, status tinyint NOT NULL DEFAULT 1 COMMENT 状态1正常0停售, PRIMARY KEY (id), UNIQUE KEY uk_flight_no (flight_no), KEY idx_city_time (departure_city, arrival_city, departure_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT航班表; -- 订单表 CREATE TABLE orders ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键, order_no varchar(32) NOT NULL COMMENT 订单号, user_id int NOT NULL COMMENT 下单用户ID, flight_id bigint NOT NULL COMMENT 航班ID, passenger_name varchar(50) NOT NULL COMMENT 乘客姓名, passenger_id_card varchar(18) NOT NULL COMMENT 乘客身份证号, seat_class varchar(20) DEFAULT 经济舱 COMMENT 舱位, price decimal(10,2) NOT NULL COMMENT 成交价快照, status tinyint NOT NULL DEFAULT 0 COMMENT 状态0待支付 1已支付 2已出票 3已取消 4已退票, create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT 下单时间, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;初始化数据也顺手给几条方便测试查询和下单流程。管理员账号 admin普通用户 zhangsan航班加两个热门航线余票库存给几个不同值方便测试INSERT INTO user (username, password, real_name, phone, id_card, role) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 系统管理员, 13800000000, 110101199001011234, 0), (zhangsan, e10adc3949ba59abbe56e057f20f883e, 张三, 13912345678, 110101199505052345, 1); INSERT INTO flight (flight_no, airline, departure_city, arrival_city, departure_time, arrival_time, price, stock, status) VALUES (CA1234, 中国国航, 北京, 上海, 2025-07-01 08:00:00, 2025-07-01 10:15:00, 1280.00, 150, 1), (MU5678, 东方航空, 上海, 广州, 2025-07-01 14:30:00, 2025-07-01 17:00:00, 980.00, 0, 1), (CZ9012, 南方航空, 广州, 成都, 2025-07-02 09:20:00, 2025-07-02 11:45:00, 860.00, 80, 1);这里的密码 e10adc3949ba59abbe56e057f20f883e 就是 123456 的 MD5 值演示项目直接这样写方便测试时统一用 123456 登录。2.4 索引设计查询快慢的差别在这索引设计不是建完表就完事要结合业务查询场景来定。我最开始的版本只建了主键结果航班查询一多数据量上来以后查询明显变慢后来加了索引才解决。用户表上 username 唯一索引既保证账号唯一也加速登录查询。这个索引是必加的登录接口基本每次都按 username 查。航班表上我建了联合索引idx_city_time字段顺序是departure_city, arrival_city, departure_time。因为系统里最常见的查询是“从北京到上海7月1日有没有票”这种组合条件走联合索引效率最高。这里注意索引字段顺序很重要等值条件的城市放前面范围条件的日期放后面否则索引利用率会变差。订单表上 user_id 加普通索引因为用户订单列表是高频查询按 user_id 查订单是必然操作。另外订单号 order_no 要加唯一索引同时还能兜底防止订单号重复插入。说到底索引不是越多越好每个索引都有写入开销你要针对真实查询场景来设计。新手最容易犯的错就是给所有字段都加索引结果数据一多写入反而变慢。一般一张表 3~5 个索引足够。3. 后端核心业务实现3.1 注册登录与权限控制登录这块我讲讲思路代码给你核心片段。密码我用了 MD5 加盐处理。严格来说 MD5 不算安全加密演示项目够用真实生产建议换成 BCrypt。关键代码逻辑是这样// 登录 public User login(String username, String password) { String md5Pwd DigestUtils.md5DigestAsHex(password.getBytes()); User user userMapper.findByUsername(username); if (user ! null user.getPassword().equals(md5Pwd)) { return user; } return null; }Controller 层校验通过后把用户对象放到 Session 里PostMapping(/login) public String doLogin(String username, String password, HttpSession session, Model model) { User user userService.login(username, password); if (user null) { model.addAttribute(error, 用户名或密码错误); return login; } session.setAttribute(loginUser, user); return redirect:/flight/list; }光登录还不够下单和订单查询接口必须做拦截不然用户不登录也能调接口这就是严重的越权问题。我用 Spring MVC 拦截器实现登录校验没登录就跳回登录页public class LoginInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); if (session.getAttribute(loginUser) ! null) { return true; } response.sendRedirect(request.getContextPath() /login); return false; } }配置拦截器时注意静态资源要放行不然后台页面 CSS、JS 全被拦了Configuration public class WebConfig implements WebMvcConfigurer { Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns(/**) .excludePathPatterns(/login, /register, /css/**, /js/**, /images/**); } }3.2 航班多条件查询航班查询接口是用户用得最多的条件包含出发城市、到达城市、出发日期三个条件都不固定。MyBatis 里用动态 SQL 拼装条件比 JPA 更直观select idsearchFlights resultTypecom.airline.entity.Flight SELECT * FROM flight where if testdepartureCity ! null and departureCity ! AND departure_city #{departureCity} /if if testarrivalCity ! null and arrivalCity ! AND arrival_city #{arrivalCity} /if if testdepartureDate ! null AND DATE(departure_time) #{departureDate} /if AND status 1 /where ORDER BY departure_time /select这里有个关键点前端传过来的日期是字符串2025-07-01后端接收时要转成LocalDate并且日期类型要用DateTimeFormat注解指定格式否则 Spring 解析不了GetMapping(/flight/search) public String search(RequestParam(required false) String departureCity, RequestParam(required false) String arrivalCity, RequestParam(required false) DateTimeFormat(pattern yyyy-MM-dd) LocalDate departureDate, Model model) { ListFlight flights flightService.search(departureCity, arrivalCity, departureDate); model.addAttribute(flights, flights); return flight_list; }所有搜索条件我都设置成 required false用户只填一个条件也能查这样体验更好。查询出来之后余票为 0 的航班直接在前端禁用下单按钮避免用户点进去才知道没票。3.3 下单与库存扣减的事务处理下单是整个项目最核心的业务也是最容易出 bug 的地方。我先说最终方案再解释为什么。Transactional(rollbackFor Exception.class) public Order createOrder(Integer userId, Long flightId, String passengerName, String passengerIdCard) { // 1. 查询航班是否存在且正常 Flight flight flightMapper.findById(flightId); if (flight null || flight.getStatus() ! 1) { throw new RuntimeException(航班不存在或已停售); } // 2. 扣减库存通过 stock 0 条件防止超卖 int rows flightMapper.deductStock(flightId); if (rows 0) { throw new RuntimeException(余票不足下单失败); } // 3. 生成订单并保存 Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setFlightId(flightId); order.setPassengerName(passengerName); order.setPassengerIdCard(passengerIdCard); order.setPrice(flight.getPrice()); order.setStatus(0); orderMapper.insert(order); return order; }扣减库存对应这句 SQL是整个防超卖的关键update iddeductStock UPDATE flight SET stock stock - 1 WHERE id #{flightId} AND stock 0 /update先解释Transactional为什么必须加。如果库存扣了订单插入却失败没有事务的话数据就脏了。加了事务注解两步要么都成功要么都回滚。这里我用的是 Spring 声明式事务注意默认只回滚运行时异常所以 rollbackFor 指定了 Exception.class保证任何异常都回滚。再解释防超卖。如果你先SELECT stock FROM flight WHERE id ...在代码里判断 stock 0 再 UPDATE两个用户同时查询都看到 stock 1就会出现两个人同时下单买同一张票最终库存变成 -1。我把判断条件直接写进 UPDATE 的 WHERE 子句里这条 SQL 执行时会对该行加行锁第二个事务必须等第一个提交后才能执行此时 stock 已经变成 0影响行数为 0抛异常回滚。这种写法叫乐观锁或条件更新比 synchronized 锁在应用层更靠谱因为它是数据库层面保证的。订单号生成我用的是时间戳加随机数yyyyMMddHHmmss加四位随机数同时数据库有唯一索引兜底万一极端情况撞了唯一索引插入报错事务回滚。实际项目里订单号还会加入用户 ID、业务类型等我这里保持简单。3.4 订单状态管理与后台功能订单状态我用 0 到 4 五个数字表示状态的流转必须清晰。0 待支付可以主动取消变成 3可以支付变成 11 已支付可以由管理员出票变成 2也可以申请退票变成 4已出票的状态基本就锁定了。这里我把状态流转控制放在 Service 层前端只展示当前状态和可操作按钮不直接修改状态值。这样状态流转移到一个地方管理不会出现在 JSP 里到处写死状态逻辑的的情况。后台管理端我做了两个功能航班管理、订单管理。航班管理支持新增航班、修改航班信息、上下架航班对应普通 CRUD但要注意删除航班前要检查有没有关联订单有订单的航班不能直接删建议用下架代替删除。订单管理主要展示所有订单列表管理员可以对已支付订单执行出票操作状态从 1 变成 2。这里有个细节很容易被忽略用户点击“支付”和“确认出票”时接口要判断当前状态是否符合预期。比如订单已经是已支付状态用户重复点支付后端应该返回“订单状态异常”而不是重复扣款。这也是面试喜欢问的场景。4. 前端页面与交互细节4.1 页面方案选择JSP Bootstrap前端我选了 JSP Bootstrap 4。这一层没有太复杂的逻辑但页面的交互细节直接决定项目能不能顺利演示。JSP 可以直接用 JSTL 标签在后端渲染数据不需要额外写 Ajax 请求对初学者最友好。Bootstrap 负责把页面做得像样不至于看起来像 2005 年的网页。页面文件放在src/main/webapp/WEB-INF/jsp/目录下需要配置视图解析器Spring Boot 在 application.properties 里这样写spring.mvc.view.prefix/WEB-INF/jsp/ spring.mvc.view.suffix.jsp4.2 页面模块与功能对照页面文件对应功能核心操作login.jsp用户登录表单校验错误提示register.jsp用户注册用户名是否重复flight_search.jsp首页航班搜索三个条件查询flight_list.jsp航班列表余票展示下单入口order_confirm.jsp订单确认乘客信息填写order_list.jsp用户订单中心支付、取消、退票admin_flight.jsp后台航班管理增删改查admin_order.jsp后台订单管理出票操作页面数量不多但每个页面都有值得注意的交互细节。比如航班列表页余票为 0 时下单按钮要禁用并显示“售罄”不能让用户点了再报错订单列表页只有待支付状态显示“去支付”和“取消订单”已支付状态才显示“申请退票”。这些状态控制你可以用 JSTL 的c:if标签判断非常方便。4.3 表单提交、数据回显与状态控制航班搜索表单是一个 GET 请求携带出发城市、到达城市、出发日期三个参数。这里用 GET 而不是 POST原因是查询操作是幂等的GET 还方便分享链接而且刷新页面不会弹出“确认重新提交表单”的提示form action/flight/search methodget input typetext namedepartureCity placeholder出发城市 input typetext namearrivalCity placeholder到达城市 input typedate namedepartureDate button typesubmit搜索航班/button /form搜索完成后页面要把用户提交的条件回显到表单里不然用户想改一个城市重新搜索发现输入框全空了体验极差。回显方式很简单用 JSTL 的${param.departureCity}把参数值填回 value 属性。订单状态按钮的显示逻辑我用一个例子展示。订单列表页每行订单根据状态显示不同按钮c:choose c:when test${order.status 0} a href/order/pay/${order.id}去支付/a a href/order/cancel/${order.id}取消订单/a /c:when c:when test${order.status 1} a href/order/refund/${order.id}申请退票/a /c:when c:when test${order.status 2} span classtext-success已出票/span /c:when c:otherwise span classtext-muted已取消/已退票/span /c:otherwise /c:choose这里的除法要做一次后端校验不能只靠前端藏按钮保证安全。用户手动构造请求直接访问/order/cancel/1完全可能后端 Service 里必须再次判断订单状态是否符合操作条件否则就是业务漏洞。很多新手项目功能演示没问题一被深挖就垮往往就是这种状态校验漏了。5. 本地部署、启动与常见错误排查5.1 开发环境准备清单先把工具列表摆出来没装好的先装好这一步绕不开工具版本用途JDK1.8Java 运行环境装完记得配 JAVA_HOME 和 PATHMySQL5.7 或 8.0数据存储IDEA2022开发工具Maven3.6依赖管理与构建Navicat任意版本可视化操作数据库JDK 和 MySQL 的安装教程网上很多这里不展开了就提醒两个高频坑JDK 装完在命令行敲 java -version 验证一下没反应基本都是环境变量忘了配MySQL 安装过程中如果让你选密码强度建议选普通强度便于测试时记住密码。5.2 从零启动项目的完整步骤新建 Spring Boot 项目时勾选 Spring Web、MyBatis Framework、MySQL Driver 三个依赖然后把上一章的建表 SQL 导入新建的 airline 数据库再写配置文件最后运行启动类。启动成功后浏览器访问http://localhost:8080/flight/list能看到航班列表页就说明项目骨架没问题。我把关键的 application.yml 配置放在这里注意 MySQL 8.0 和 5.7 的配置有差异一会儿单独讲server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/airline?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.airline.entity如果要用 Maven 打包部署命令行执行mvn clean package -Dmaven.test.skiptrue打出来的 jar 包直接java -jar target/airline-0.0.1-SNAPSHOT.jar就能跑。打包部署这一步建议至少做一次很多面试官会问“你项目怎么部署的”能答出 jar 包运行比只会 IDEA 点运行强不少。5.3 常见问题与解决方案速查表现象可能原因解决方案数据库连接失败报 Access denied用户名密码错误或账号权限不足检查 yml 里的账号密码手动连一次 Navicat 确认报 Communications link failureMySQL 服务没启动启动 MySQL 服务或检查端口是否 3306页面中文乱码数据库连接串缺少编码参数url 加 useUnicodetruecharacterEncodingutf8Mapper 绑定的 SQL 找不到mapper-locations 路径配置错误检查 mapper xml 是否在 classpath 指定目录下8080 端口被占用其他进程占用端口换端口server.port8081或杀掉占用进程前台页面样式全丢拦截器拦截了静态资源放行 /css/、/js/等路径LocalDate 类型格式解析失败前端日期格式后端不认参数加 DateTimeFormat(pattern yyyy-MM-dd)Maven 依赖下载失败网络问题或镜像没配配置阿里云 mirror重新导入项目启动报 Servlet 初始化异常JSP 依赖没加全添加 tomcat-embed-jasper 依赖下单后库存没扣或扣了没订单事务没生效Service 实现类加 Transactional异常回滚检查5.4 MySQL 5.7 和 8.0 的版本差异坑如果装的是 MySQL 8.0有两个地方要改驱动类名从com.mysql.jdbc.Driver改成com.mysql.cj.jdbc.Driver连接 URL 必须加serverTimezoneAsia/Shanghai否则会报时区错误。Maven 依赖里 mysql-connector-java 的版本也会自动跟随 Spring Boot 版本8.0 的 MySQL 对应连接器 8.x没问题。还有个老问题就是 Navicat 连不上 MySQL 8.0报 1251 错误。原因是 MySQL 8.0 默认缓存认证插件是 caching_sha2_passwordNavicat 老版本不支持最简单的解决方案是执行这条 SQL把 root 用户的认证方式改回 mysql_native_passwordALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;另外5.7 和 8.0 的 SQL 基本兼容这个项目在两个版本下都能跑核心逻辑不受影响。如果你跟着教程装的早期版本连接器记得在 IDEA 的 Maven 窗口里刷新一下让依赖版本对齐。6. 做完这个项目后我的一些体会这个项目我前前后后带人做过好几次每次做完都会发现新的东西。第一版的时候我只把流程跑通就没有继续优化后来复盘才发现少了拦截器用户不登录直接访问下单接口也能下单这个漏洞在面试中一问一个准。所以我后来每一版都会把权限控制、状态校验、事务完整性这些从代码里抠出来单独检查一遍。我个人最深的体会是别小看这个项目它虽然表只有三张但包含的业务设计思想并不少。库存扣减的并发处理、订单状态机的流转、乘客信息冗余快照、索引设计每一个都是真实项目里的高频考点。你把这个项目里这些问题真正想明白远比照着视频敲一个看起来高大上的商城系统更有收获。如果你已经把这个版本跑通了想再往深做我建议三条路第一把航班查询结果用 Redis 做缓存减少数据库压力第二前端换成 Vue 3 Axios后端改成纯接口返回 JSON练前后端分离第三给支付模块接入支付宝沙箱或者微信支付沙箱让订单状态流转有真实支付回调。这三条路按自己精力选一条就够项目质感和面试可聊的东西都会明显提升。本文还有配套的精品资源点击获取
返回列表