简介这套基于JavaEE与MySQL的酒店管理系统毕业设计资料包内容围绕完整的客房业务场景设计适合具备一定编程基础的学生和开发者将其作为毕业设计、课程设计或工程实训的参考项目。压缩包为ZIP格式整体大小约186.81MB涵盖完整的JavaEE项目源码、MySQL数据库初始化脚本、毕业论文、答辩PPT以及演示视频等主要文件类型便于从开发、文档到答辩全流程使用。项目功能覆盖客户管理、客房管理、菜品管理、餐桌预定和餐饮消费管理并给出了JDK与Tomcat以及MySQL环境下的具体部署方案降低了复现与二次开发难度。数据库脚本可独立导入MySQL快速搭建可运行环境。已有156人浏览学习借助源码、论文和视频可以掌握酒店管理系统的模块划分、数据库表结构设计与业务实现思路同时适合作为论文写作和答辩准备的参考模板。1. 基于javaEEMySQL的酒店管理系统毕设选题里的老牌刚需选题时最怕的是“听起来很新做起来没底”。酒店管理系统在 javaEE 方向里属于典型的老牌刚需需求不复杂但房费计算、订单状态、会员折扣这些业务逻辑能把 CRUD 做出真实感而且数据库表关系复杂度适中拿来当毕业设计正好。标题里那套源码、数据库 SQL、论文、答辩 PPT 和演示视频之所以成为经典配置是因为大多数人的痛点不是写不出代码而是不知道业务链路怎么闭环、答辩怎么讲。这篇笔记把主题锁定在基于 javaEEMySql 酒店管理系统的完整落地路径上从技术选型、建库建表、核心功能实现到部署排查和答辩亮点按我实际带项目的顺序写给你。适合正在做酒店管理系统毕业设计的人也适合想用这个老题目补齐分层架构和数据库基本功的初级开发者。2. 技术选型与项目骨架javaEEMySQL的组合为什么最稳2.1 三种方案的分寸感Servlet/JSP、SSH、SSM到底该选谁拿到这个标题第一个要拍板的就是主框架。市面上这类系统的主流做法有三种纯 Servlet JSP JDBC、SSHStruts2 Spring Hibernate、SSMSpring SpringMVC MyBatis。不少同学一看 SSH 是“当年企业标配”就扑上去结果被 Struts2 的拦截器配置和 Hibernate 的懒加载坑到怀疑人生。我的建议很直接论文题目写的是 javaEE 而不是 SpringBoot说明评审期待的是你能讲清分层架构和 Java Web 原生运行机制纯 Servlet JSP JDBC/连接池反而是最稳妥的答案。为什么这么说酒店管理系统的核心是订单和房态这类强业务逻辑场景里Servlet 负责接收请求、调用 Service、控制页面跳转JSP 只负责渲染这个 MVC 结构在答辩时三句话就能讲完。SSH 里的 Hibernate 映射关系在学习成本上占比太高Struts2 的 valueStack 又是个黑匣子一旦页面取值取不到排查链路非常长。SSM 当然也可以但 MyBatis 的 XML 映射文件会分散你对业务设计的注意力而且框架版本之间经常出现 jar 包冲突不少人的毕设时间就耗在“启动报错—搜依赖—换版本”这个循环里。用原生 Servlet 还有一个隐藏优势它最能体现你理解 HTTP 请求的生命周期。SpringBoot 固然是当前主流但如果你在毕业设计里用 SpringBoot评委大概率会追问“那 javaEE 的 Servlet、Filter、Listener 你还熟吗”到时候你如果只能说“框架封装了”场面会比较被动。我的做法是主链路用 Servlet JSP JDBC Druid 连接池把 Spring 的 IoC 思想用在 Service 层的接口与实现分离上这样既有原生味道又能体现设计意识。2.2 从 JDK 到 Tomcat跑通项目前的三个环境检查环境准备这一步看着基础但翻车率极高。我见过太多次“代码没问题环境不对”的案例尤其是 MySQL 8 和 Tomcat 版本组合出的幺蛾子。下面这套检查顺序是我每次搭 javaEE 环境都会走的java -version mysql -u root -p -e status; cd /path/to/tomcat/bin ./catalina.sh run第一条确认 JDK 版本javaEE 项目用 JDK 8 或 11 最稳JDK 17 配老 Tomcat 偶尔会碰到模块访问限制。第二条确认 MySQL 服务是真的活着而不是只在安装向导里“看起来装好了”。第三条直接前台启动 Tomcat能看到完整日志比双击 startup.bat 一闪而过强太多。如果你用 VSCode 配 javaEE 语言环境记得把 Tomcat 的 lib 目录加进 referenced libraries否则 Servlet 类会飘红。Tomcat 版本这里提前打个预防针如果用 Tomcat 10 或更高Servlet 的包名已经从 javax.servlet 换成了 jakarta.servlet很多老源码直接跑会 ClassNotFound。这篇标题对应的项目源码大概率是按 Tomcat 8/9 写的所以我建议你直接用 Tomcat 9省去一批包名改造工作。项目放对位置也很关键webapps/ROOT还是webapps/你的项目名会影响访问路径我习惯放在webapps/hotel下然后通过http://localhost:8080/hotel访问这样后续部署到云服务器时路径不用改。2.3 web.xml 是入口说明书Servlet 映射与全局配置打开 javaEE 老项目的第一件事先看 web.xml。这个文件决定了哪些请求进哪个 Servlet、静态资源怎么放行、默认首页是谁。下面是我常用的最小配置web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee version3.1 servlet servlet-nameLoginServlet/servlet-name servlet-classcom.hotel.web.LoginServlet/servlet-class /servlet servlet-mapping servlet-nameLoginServlet/servlet-name url-pattern/login.do/url-pattern /servlet-mapping welcome-file-list welcome-fileindex.jsp/welcome-file /welcome-file-list /web-app这里有两处值得注意。servlet-class必须和源码里的包名完全一致大小写都不能错否则启动时不报错访问时才 404。url-pattern尽量用/login.do这种精确匹配不要图省事写/*因为/*会拦截所有请求包括 JSP 页面本身的渲染请求造成“所有页面白屏只有 Servlet 正常”的诡异现象。Servlet 版本 3.1 对应 Tomcat 8/9如果你用注解WebServlet(/login.do)替代 web.xml 配置也可以但论文里最好把 web.xml 的写法也放上去因为它是 javaEE 规范的直观体现。2.4 数据库连接配置Druid 连接池与 jdbc.properties数据库连接这块最忌讳在 DAO 里DriverManager.getConnection()每次现连。酒店管理系统要扛住前台高频查询连接池是必须的。常见做法是用 Druid配置写在jdbc.properties里便于替换环境jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hotel_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456 jdbc.maxActive10 jdbc.initialSize2URL 里的参数每一个都是血泪教训。serverTimezoneAsia/Shanghai不写MySQL 8 会直接报时区错误useSSLfalse是关掉本地开发环境的 SSL 证书校验避免驱动每次连接都去验证证书allowPublicKeyRetrievaltrue解决 MySQL 8 的 caching_sha2_password 认证插件在非安全连接下取公钥的问题。characterEncodingutf8必须写在 URL 里不然哪怕数据库和页面都是 UTF-8JDBC 传输过程中也会乱码。配套的工具类一般是静态代码块加载配置并创建数据源public class DbUtil { private static DruidDataSource ds; static { try { Properties p new Properties(); p.load(DbUtil.class.getClassLoader() .getResourceAsStream(jdbc.properties)); ds (DruidDataSource) DruidDataSourceFactory.createDataSource(p); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConn() throws SQLException { return ds.getConnection(); } }ResourceAsStream的路径是 classpath 根目录所以jdbc.properties必须放在 src 根目录或 resources 目录下放错位置会在静态块里抛空指针这种启动报错最容易让人误判成“包没导全”。连接池的maxActive不要贪大本地开发 10 个足够设成 50 反而会让 MySQL 的线程数飙升拖慢整机响应。3. 数据库设计先行把酒店业务钉进 MySQL 的 7 张核心表3.1 从业务流程到表结构先理清角色和状态再建表酒店管理系统的数据库设计核心是回答四个问题谁在用系统、房间有什么状态、订单怎么流转、退房怎么算钱。我习惯把角色拆成三类系统管理员管房型和房间信息前台负责预订、入住、退房和结账经理看营业报表。围绕这些角色核心表可以收敛为 7 张系统用户表sys_user、会员表t_member、房型表t_room_type、房间表t_room、预订订单表t_order、入住登记表t_checkin、消费明细表t_consume。房型和房间为什么要拆两张表因为“价格”和“房间”是一对多的关系如果把价格直接写进房间表改一次房价就要 UPDATE 几十行而且容易漏。下面是一个精简但完整的建表片段CREATE TABLE t_room_type ( id INT PRIMARY KEY AUTO_INCREMENT, type_name VARCHAR(20) NOT NULL COMMENT 单人间/双人间/套房, base_price DECIMAL(10,2) NOT NULL COMMENT 基准价/晚, hourly_price DECIMAL(10,2) DEFAULT 0 COMMENT 钟点房价/小时, bed_num TINYINT DEFAULT 1 COMMENT 床位数, breakfast TINYINT DEFAULT 0 COMMENT 1含早 0不含早, status TINYINT DEFAULT 1 COMMENT 1启用 0停用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE t_room ( id INT PRIMARY KEY AUTO_INCREMENT, room_no VARCHAR(10) NOT NULL UNIQUE COMMENT 门牌号, room_type_id INT NOT NULL, floor_no TINYINT COMMENT 所在楼层, room_status TINYINT DEFAULT 0 COMMENT 0空闲 1已预订 2已入住 3维修 4脏房, remark VARCHAR(255), FOREIGN KEY (room_type_id) REFERENCES t_room_type(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;价格字段用DECIMAL(10,2)而不是 FLOAT是因为浮点数在计算房费时会出现 0.10.2 不等于 0.3 的问题金额出错在酒店这种场景里非常致命。room_status用 TINYINT 存数字而不是直接存中文是为了后续状态流转时用 int 比较和更新更高效中文状态值应该由前端字典翻译。UNIQUE约束加在room_no上防止重复门牌号。所有表统一ENGINEInnoDB因为订单和入住登记需要事务支持MyISAM 不支持行级锁和事务回滚。接下来是订单表它的设计决定了预订、入住、取消这条主链路的复杂度CREATE TABLE t_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单号格式日期随机数, room_id INT NOT NULL, cust_name VARCHAR(30) NOT NULL, cust_phone VARCHAR(20), member_id INT COMMENT 会员ID可为空, plan_in_date DATE COMMENT 预计入住日期, plan_out_date DATE COMMENT 预计离店日期, hours INT DEFAULT 0 COMMENT 钟点房小时数, order_status TINYINT DEFAULT 0 COMMENT 0已预订 2已入住 3已取消 4已离店, pre_amount DECIMAL(10,2) COMMENT 预付金额, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_room_status (room_id, order_status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;订单表里我把预计入住和预计离店拆成两个 DATE 字段而不是一个时间段字段这样查“明天有哪些房要退”只需WHERE plan_out_date DATE_ADD(CURDATE(), INTERVAL 1 DAY)索引也能用上。order_no不要用自增 ID 直接展示给客户酒店行业习惯看到日期开头的单号我一般用yyyyMMddHHmmss 三位随机数生成。最后那行INDEX很重要房态列表页最频繁的查询就是“按房间和时间过滤订单”没有这个联合索引数据量过万后会明显变慢。3.2 房态流转别用字符串裸奔状态机与条件更新房间状态是整个系统的命脉。前台操作的每一步都在改t_room.room_status空闲 → 已预订客人下单→ 已入住客人到店→ 脏房退房后保洁→ 空闲。如果代码里到处直接写UPDATE t_room SET room_status 2 WHERE id 1不出一个星期就会出现“房间明明被预订了另一个前台还能开出去”的并发问题。我的做法是在状态流转处统一走 Service 方法并且用条件 UPDATE 实现乐观锁START TRANSACTION; UPDATE t_room SET room_status 2 WHERE id #{roomId} AND room_status 1; -- 受影响行数为 0 说明房间不是“已预订”状态下单失败 INSERT INTO t_checkin (order_id, room_id, cust_name, real_in_time) VALUES (#{orderId}, #{roomId}, #{custName}, NOW()); COMMIT;这段 SQL 的精髓在AND room_status 1。如果两个前台同时操作同一间房MySQL 的行锁会让后到的 UPDATE 等待但等锁释放后它的 WHERE 条件已经匹配不上最新状态受影响行数为 0代码里就能捕获到这个失败并提示“房间状态已变化”。这就是用数据库自身的能力做并发控制比在 Java 代码里加 synchronized 靠谱得多也更容易在答辩时讲出亮点。另外强调一个容易忽略的细节脏房状态一定要有。客人退房后房间需要保洁如果直接置为“空闲”下一个客人入住时发现房间没打扫就是事故。我见过不少毕设把状态只做成“已入住/空闲”两态业务上其实是不完整的。哪怕你这个系统的保洁模块只是一个按钮也要有“一键标记脏房”和“保洁完成恢复空闲”这两个动作这会让评审觉得你想到了真实场景。3.3 导入导出 SQL 脚本导入前先建库外键别硬刚源码包里通常附带一个hotel.sql或db.sql这是整个项目的数据库资产。拿到手先别急着source我一般按下面这套流程走mysql -u root -p -e CREATE DATABASE IF NOT EXISTS hotel_db DEFAULT CHARACTER SET utf8mb4; mysql -u root -p hotel_db hotel_db.sql第二条命令前必须先建库因为脚本里多数只包含表和数据的 INSERT不会帮你建库。如果脚本没有指定USE hotel_db;导入时会一直报“No database selected”。另外很多脚本开头有DROP TABLE IF EXISTS这本身没问题但如果表之间有外键约束DROP 父表时会失败。遇到这种情况先执行一句SET FOREIGN_KEY_CHECKS0;再导导完再设回 1。这个坑在 Workbench 里特别常见明明点了 Forward Engineer生成的脚本里 DROP 顺序却是乱的。导出备份时用mysqldump更专业一些尤其答辩前一定要备份一次mysqldump -u root -p hotel_db hotel_db_backup.sql如果是用 MySQL Workbench 管理表结构注意导出时勾选 “Dump Stored Procedures and Functions” 和 “Dump Events”否则你在 Workbench 里建的存储过程不会进到脚本里。字符集方面建议建库时一律utf8mb4不要用utf8MySQL 的 utf8 实际是 utf8mb3存不了 emoji 和部分生僻汉字客人姓名里有生僻字时页面就会显示成问号。4. 代码主线从登录到退房结账的完整链路实现4.1 登录与权限一个 Session 撑起的最简 RBAC登录是整个系统的大门也是答辩时评委必看的第一段代码。常见做法是在login.jsp收集用户名和密码提交到LoginServlet验证成功后把用户信息放进 Session。注意两点密码不能明文存库前端传过来的密码要做哈希后比对DAO 层必须用 PreparedStatement 防 SQL 注入。核心代码如下WebServlet(/login.do) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding(UTF-8); String username req.getParameter(username); String password Md5Util.hash(req.getParameter(password)); SysUser user new UserDao().findByNameAndPwd(username, password); if (user ! null) { HttpSession session req.getSession(); session.setAttribute(loginUser, user); session.setAttribute(role, user.getRole()); resp.sendRedirect(req.getContextPath() /index.jsp); } else { req.setAttribute(msg, 账号或密码不正确); req.getRequestDispatcher(/login.jsp).forward(req, resp); } } }sendRedirect和forward的区别要搞清楚登录成功后重定向是为了避免刷新页面时重复提交表单登录失败后转发是为了把msg带回到登录页显示。DAO 层里的查询语句务必用占位符String sql SELECT * FROM sys_user WHERE username? AND password?; try (PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, username); ps.setString(2, password); try (ResultSet rs ps.executeQuery()) { if (rs.next()) { ... } } }为什么不直接String sql SELECT * FROM sys_user WHERE username username 因为前台在用户名框里输入 OR 11就能绕过密码直接登录这在答辩现场是致命伤。做了 PreparedStatement 之后评委如果追问安全加固你还可以补一句“我对密码做了加盐哈希并对 Session 里的角色做了 Filter 拦截”。4.2 预订到入住事务边界和房间锁定的实战写法预订到入住这条链路是最能体现工程能力的部分。业务流程是前台选择房型 → 提交订单 → 系统把房间从“空闲”改为“已预订” → 客人到店后“入住登记” → 订单状态从“已预订”改为“已入住”。这两步必须在一个事务里否则会出现订单插入成功但房间状态没变或者房间被占但订单丢失的情况。public boolean createOrder(Order order) { Connection conn null; try { conn DbUtil.getConn(); conn.setAutoCommit(false); RoomDao roomDao new RoomDao(); int rows roomDao.updateStatus(2, 1, order.getRoomId()); if (rows 0) { conn.rollback(); return false; } orderDao.insert(order); conn.commit(); return true; } catch (SQLException e) { if (conn ! null) { try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } } throw new RuntimeException(预订下单失败, e); } finally { if (conn ! null) { try { conn.setAutoCommit(true); conn.close(); } catch (SQLException e) { } } } }这段代码里roomDao.updateStatus(2, 1, order.getRoomId())对应的是UPDATE t_room SET room_status 2 WHERE id ? AND room_status 1受影响行数为 0 说明房间状态已经不是“空闲”直接回滚避免脏数据。事务边界要覆盖“改状态”和“插订单”两个动作少了任何一个都不完整。最后在 finally 里把autoCommit恢复成 true 再关连接是因为连接池里的连接是复用的如果不重置状态下一次拿到这条连接的人会在一个残留的事务里执行 SQL查不到数据时很难排查。4.3 退房结账算法统一按自然日计费别用浮点数算钱退房结账是酒店管理系统里最有“业务含金量”的部分。房费怎么算业界常见做法是“按自然日计费”即入住当天算一天离店日的中午 12 点前退房不额外收费超过 12 点加收半天或一天房费。你不需要做得和真实 PMS 一样复杂但至少要把“天数 × 房价 消费 - 折扣”这条公式写对。public BigDecimal calcAmount(Order order, ListConsumeItem items) { long days ChronoUnit.DAYS.between( order.getPlanInDate(), order.getPlanOutDate()); BigDecimal roomFee order.getBasePrice() .multiply(BigDecimal.valueOf(days)); BigDecimal total roomFee; for (ConsumeItem item : items) { total total.add(item.getAmount()); } if (order.getMember() ! null) { total total.multiply(order.getMember().getDiscount()); } return total.setScale(2, RoundingMode.HALF_UP); }ChronoUnit.DAYS.between计算两个 LocalDate 之间的天数注意这个方法返回的是 long如果退房日等于入住日返回 0分钟房订单要走单独的分支。所有金额运算必须用BigDecimal乘法之后调用setScale(2, RoundingMode.HALF_UP)做四舍五入否则会出现 113.500000001 这种结果。会员折扣如果存在t_member表里是 0.9 这种小数做百分比换算时也要用 BigDecimal不能转 double 后再回来。这段算法建议单独抽一个PriceCalculator类不要写在 JSP 里面试时把类名报出来会比“我的代码都写在 Servlet 里”听起来专业得多。5. 避坑清单本地能跑不代表答辩能过5.1 JDBC 连 MySQL 8 报 SSL 与时区错误现象项目启动后第一次访问数据库控制台抛The server time zone value йʱ is unrecognized或者Establishing SSL connection without servers identity verification is not recommended前端页面上表现为登录按钮点了没反应后台却能看到 SQLException。原因MySQL 8 的 JDBC 驱动com.mysql.cj.jdbc.Driver默认要求带时区参数而连接串里没写同时驱动默认会去校验 SSL 证书本地自建库没有合理证书就会报警告甚至连接失败。解决在jdbc.url后追加useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue。useSSLfalse的意思是本地开发环境不走证书验证只要不是公网传输敏感数据这个参数没问题。如果用的是 MySQL 5.7驱动可以换com.mysql.jdbc.Driver或不加 serverTimezone但为了兼容性我统一按 8 的写法来。另外确认mysql-connector-java的 jar 包版本和 MySQL 服务端版本不要差太远5.x 的驱动去连 8.x 服务端会直接报Unable to load authentication plugin caching_sha2_password。5.2 连不上本地 MySQLsocket 文件、端口和认证插件三连现象命令行执行mysql -u root -p报ERROR 2002 (HY000): Cant connect to local MySQL server through socket /tmp/mysql.sock或者 Java 代码里报Communications link failure。原因这条报错的意思是客户端默认通过 unix socket 文件连本地库但/tmp/mysql.sock不存在最直接的原因是服务没启动其次是 MySQL 的 socket 文件路径被改过。如果你在 Java 里用localhost连接驱动也会优先走 socket 而不是 TCP。解决先确认服务真的活着systemctl status mysql mysqladmin -u root -p status如果服务活着但 socket 路径不对一条路是找到my.cnf里socket的路径另一条更省事的办法是在 JDBC URL 里把localhost改成127.0.0.1强制走 TCP 端口绕开 socket 文件。按下葫芦浮起瓢的情况也有明明 MySQL 起来了端口却是 3307这种多实例安装导致的坑用netstat -tlnp | grep 3306看端口占用就能定位。最后还有一种排查点root 账号在 MySQL 8 默认用caching_sha2_password认证老驱动不支持时可以考虑把账号改成mysql_native_password这属于常规兼容操作。5.3 中文乱码从过滤器、连接参数到建表字符集四层排查现象登录页面输入中文用户名提交后变成“”或数据库表里看中文是乱码又或者页面能显示中文但查询条件带中文查不到数据。原因乱码是四层字符集不一致叠加的结果谁都不能独善其身。POST 请求体没有设置 UTF-8JSP 页面本身pageEncoding不对JDBC 连接串里没带characterEncodingutf8建表时用了latin1默认字符集。任何一层断了最终表现都是乱码。解决从上到下一层层堵漏。最省事的做法是加一个全局编码过滤器覆盖所有请求WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { req.setCharacterEncoding(UTF-8); resp.setContentType(text/html; charsetUTF-8); chain.doFilter(req, resp); } }有了这个过滤器POST 请求体里的中文基本稳了。连接串里的characterEncodingutf8负责 JDBC 传输层建库时DEFAULT CHARSETutf8mb4负责存储层JSP 头部的pageEncodingUTF-8负责视图层。四层都对齐后把表里已存在的乱码数据 DELETE 掉重新录入不要想着 UPDATE 能救回来乱码数据通常是不可逆的。5.4 Tomcat 10 换包名导致 404 与 ClassNotFoundjavax 和 jakarta 的分水岭现象同一套源码照着教程配 Tomcat 10项目能启动但访问 Servlet 路径时 404后台报java.lang.NoClassDefFoundError: javax/servlet/ServletException或ClassNotFoundException: javax.servlet.http.HttpServlet。原因Tomcat 10 开始全面迁移到 Jakarta EE 9Servlet API 的包名从javax.servlet改成了jakarta.servlet。老项目源码里的import javax.servlet.*在 Tomcat 10 的 classpath 里找不到对应类但 Tomcat 本身又用了一些兼容机制所以启动阶段不一定报错直到真正加载 Servlet 类才炸。解决两条路。第一直接换回 Tomcat 9这是最不折腾的方案源码里所有javax.servlet都不用动JDK 8 或 11 都支持。第二坚持用 Tomcat 10 的话用 IDEA 或 VSCode 的全局替换把所有import javax.servlet.改成import jakarta.servlet.同时把 web.xml 里的xmlns命名空间也改成 Jakarta EE 的版本。对毕业设计来说我强烈建议选第一条路因为你现在的时间应该花在业务和答辩上而不是给 Servlet 包名做迁移。顺带提醒源码包里提供的readme.txt如果写了“运行环境Tomcat 8/9”就老老实实按它说的配别自己升级到 Tomcat 10 再花两天排错。6. 答辩展示与项目进化把演示视频和存储过程变成加分项6.1 演示视频也是技术资产按业务剧本录别录操作流水账标题里那套视频资源别只把它当作“给老师看一眼的东西”。我的经验是越是这种成套交付的毕设越要自己重录一版演示视频因为别人的视频里项目名可能和你的论文题目不一致。录制时按“预订 → 入住登记 → 消费记账 → 退房结账 → 营业报表”这条业务链来剪每个步骤 3 到 5 秒的停留即可后台代码不用录进去但数据库表要切出来看一眼让评委知道数据是真实落库的。视频里最好单独录一段异常操作比如重复预订同一间房时系统的报错提示这在答辩时主动讲出来比被动回答“并发怎么办”更有说服力。6.2 加一个 MySQL 存储过程做日报汇总马上拔高项目档次如果你的代码包里没有存储过程强烈建议加一个。酒店管理系统天然适合用存储过程做夜间统计因为房间数有限统计逻辑固定不需要高并发数据库端跑批量任务正合适。比如生成每日营业报表DELIMITER $$ CREATE PROCEDURE proc_daily_report(IN p_date DATE) BEGIN INSERT INTO t_report (report_date, order_count, room_income, consume_income) SELECT p_date, COUNT(DISTINCT order_id), SUM(room_fee), SUM(consume_fee) FROM t_checkin WHERE DATE(create_time) p_date; END$$ DELIMITER ;在答辩时讲清楚“我为什么不用 Java 在代码里统计”因为报表统计要扫大量订单数据放到数据库端执行可以减少网络传输而且可以挂在 MySQL 事件调度器里每天凌晨自动跑。就这一句话立刻让你的项目从“CRUD 管理系统”变成“有真实工程考虑的系统”。另一个低成本亮点是用第四章讲的乐观锁房态流转把同一间房并发预订的失败场景截图放进论文配上行的锁机制解释这是评委最想看的“异常处理”素材。最后说个我做毕设辅导时的习惯每次答辩前我会把项目当产品重新过一遍专门挑那些“正常人不会点”的操作去试比如在预订页直接改成房号 999看看系统有没有报错提示把系统日期改到跨月看看报表统计是否正常。这些不是刁难自己而是提前把黑匣子摸清总比你站在台上被评委问到哑口无言强。希望这篇笔记能帮你把 javaEEMySQL 这个经典组合做成一份经得起追问的作品。本文还有配套的精品资源点击获取
