恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Servlet+JSP酒店客房预定管理系统:前后台分离实战与避坑指南

  • 首页
  • 资讯中心
  • /
  • Servlet+JSP酒店客房预定管理系统:前后台分离实战与避坑指南

相关资讯

Linux重定向精讲:从标准输入输出到2>1的底层原理与实战 2026/10/9 3:18:05
VMware摄像头打不开?USB直通与宿主机占用排查指南 2026/10/9 3:13:04
Linux文件IO与标准IO底层机制及性能实测对比 2026/10/9 3:13:04

最新资讯

DiPlay 隐私与诊断数据模型:本地优先的连接设计、报告脱敏与 Usage Access 边界
SpringBoot+Vue3博物馆展览门户系统设计与实现
Java高级开发面试全攻略:从集合源码到分布式实战
opencode双会话内核与事件溯源架构解析
.NET RyuJIT如何让struct成为一等公民:从栈优化到寄存器级性能革命
Solidity存储与内存管理:Storage、Memory、Calldata与Event实战解析

今日推荐

AI编程智能体实战:从写代码到指挥代码的架构与落地
多模态大模型全栈能力拆解:从数据对齐到弹性推理
大模型Agent开发入门:从工具调用循环到落地避坑指南

本周热门

MR25H40CDF + PIC18F65K40:工业记录仪高可靠存储实战
基于STM32的数控恒压恒流电源设计:从硬件到PID调参全解析
LT9211 MIPI重定时器原理与双路扇出实战指南

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Servlet+JSP酒店客房预定管理系统:前后台分离实战与避坑指南

发布时间:2026/10/9 3:18:05
Servlet+JSP酒店客房预定管理系统:前后台分离实战与避坑指南 简介这是一套面向计算机专业学生与Java Web入门者的酒店客房预定管理系统实战项目采用Servlet与JSP构建前后端配合Tomcat服务器和MySQL数据库可直接作为毕业设计或课程设计参考。项目分为用户与管理员两大模块用户端支持注册登录、预约房间、客房搜索、在线留言与预定记录查询管理端涵盖客房及分类管理、会员管理、留言管理、剩余房间与订房信息查询功能链路完整适合练习MVC分层与数据库交互。资源包共170个文件约1.21MB包含41个java源文件、41个class编译文件、34个jsp页面以及gif、jpg图片素材、jar依赖、xml配置和sql数据库脚本源码与脚本齐备导入IDE后即可调试运行。目前已有340人学习下载可作为理解Servlet请求处理、会话管理与JSP页面渲染的完整案例帮助读者快速掌握Java Web项目结构与开发流程。1. 酒店客房预定管理系统为什么 ServletJSP 的前后台分离值得你亲手搭一遍很多做了两三年 CRUD 的同学一提到酒店客房预定管理系统第一反应是“不就是几张表加增删改查”。但真动手把前后台拆开跑一遍你会发现事情没那么简单房态要实时联动、订单要防超卖、后台改价要立刻反映到前台展示、不同角色的菜单权限还得各管各的。ServletJSP 这套看起来“老”的技术栈恰恰是理解请求流转、会话管理和 MVC 分层最直白的载体。前台面向住客负责房型浏览、日期筛选、下单和订单查询后台面向管理员负责房态维护、订单审核、价格调整和用户管理。两套入口共用一套领域模型但视图和权限完全隔离。这篇文章不讲空泛概念而是把表结构、Servlet 映射、JSP 页面组织、会话校验和几个必踩的坑一次讲透适合想拿一个完整项目练手、或者正在做课程设计需要落地细节的人。2. 前后台到底怎么分从需求到表结构的落地拆解2.1 前台与后台的职责边界先把“前后台”这个词说清楚。这里的“前后台”不是前端框架意义上的前后端分离而是同一套 Web 应用里面向两类用户的两组页面与控制器。前台是住客侧首页展示房型列表、按入住日期和离店日期筛选可用房间、填写预订信息、生成订单、查询自己的订单状态。后台是管理侧管理员登录后查看所有订单、修改房态空闲/已预订/入住中/维护中、调整房型价格、管理用户账号。两者共用同一套数据库但访问路径和权限校验不同。常见做法是用一个web.xml或注解方式把/admin/*路径统一拦截做管理员会话校验而/room/*、/order/*等前台路径只校验普通用户会话。这样做的直接好处是后台的 JSP 页面即使被猜到路径没有管理员会话也进不去。从工程结构上看我一般会这样组织src/ com.hotel.entity/ // Room, Order, User, RoomType com.hotel.dao/ // RoomDao, OrderDao, UserDao com.hotel.service/ // RoomService, OrderService com.hotel.servlet/ // 前台与后台的 Servlet com.hotel.filter/ // 编码过滤器、权限过滤器 WebContent/ front/ // 前台 JSP admin/ // 后台 JSP static/ // css、js、图片这个结构不复杂但能清晰地把“谁负责什么”固定下来。实体类对应表DAO 只做 SQLService 做业务判断比如下单前检查房态Servlet 只做参数接收和页面跳转。2.2 核心表结构与字段说明酒店客房预定管理系统的表不用多但几个关键字段设计错了后面改起来很痛苦。下面是我实际用下来比较稳的一套表名关键字段说明room_typeid, type_name, price, bed_count, area房型基础信息价格放这里而不是房间表roomid, room_no, type_id, statusstatus 用整数表示0空闲 1已预订 2入住中 3维护ordersid, user_id, room_id, check_in, check_out, total_price, status, create_timestatus0待确认 1已确认 2已入住 3已完成 4已取消userid, username, password, phone, rolerole0普通用户 1管理员这里有两个容易翻车的点。第一价格放在room_type而不是room因为同一房型不同房间价格通常一致改价时只改一处。第二订单表里冗余存total_price不要每次查询都去乘单价和天数因为价格会变订单生成那一刻的价格必须固化下来。日期字段用DATE类型不要用字符串。入住和离店日期在业务上只精确到天用DATE比较和计算天数都方便。计算天数时注意离店日期减入住日期就是住几晚不要加一。2.3 用 SQL 建表并插入测试数据下面这段 SQL 可以直接在 MySQL 里跑建库建表加几条测试数据CREATE DATABASE hotel_db DEFAULT CHARSET utf8mb4; USE hotel_db; CREATE TABLE room_type ( id INT PRIMARY KEY AUTO_INCREMENT, type_name VARCHAR(50) NOT NULL, price DECIMAL(10,2) NOT NULL, bed_count INT DEFAULT 1, area INT DEFAULT 20 ); CREATE TABLE room ( id INT PRIMARY KEY AUTO_INCREMENT, room_no VARCHAR(10) NOT NULL UNIQUE, type_id INT NOT NULL, status TINYINT DEFAULT 0, FOREIGN KEY (type_id) REFERENCES room_type(id) ); CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, phone VARCHAR(20), role TINYINT DEFAULT 0 ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, room_id INT NOT NULL, check_in DATE NOT NULL, check_out DATE NOT NULL, total_price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id), FOREIGN KEY (room_id) REFERENCES room(id) ); INSERT INTO room_type (type_name, price, bed_count, area) VALUES (标准大床房, 288.00, 1, 25), (双床房, 328.00, 2, 28), (豪华套房, 588.00, 1, 45); INSERT INTO room (room_no, type_id, status) VALUES (101, 1, 0), (102, 1, 0), (201, 2, 0), (202, 2, 0), (301, 3, 0); INSERT INTO user (username, password, phone, role) VALUES (admin, admin123, 13800000000, 1), (zhangsan, 123456, 13900000001, 0);建表时注意外键顺序先建room_type和user再建room和orders。测试数据里管理员账号admin的role设为 1普通用户设为 0后面权限过滤器就靠这个字段区分。2.4 日期筛选可用房间的 SQL 写法前台最核心的查询是用户选了入住和离店日期后列出这段时间内没有被占用的房间。这个查询写不对就会出现“同一间房被两个人同时订”的经典事故。思路是先找出在目标日期区间内已经存在有效订单的room_id然后从room表里排除这些房间并且只保留状态为空闲的。SELECT r.id, r.room_no, rt.type_name, rt.price FROM room r JOIN room_type rt ON r.type_id rt.id WHERE r.status 0 AND r.id NOT IN ( SELECT o.room_id FROM orders o WHERE o.status IN (0, 1, 2) AND o.check_in ? -- 用户选择的离店日期 AND o.check_out ? -- 用户选择的入住日期 );这里两个?的顺序容易搞反。判断区间重叠的条件是已有订单的入住日期小于用户离店日期并且已有订单的离店日期大于用户入住日期。只要满足这个条件就说明时间有重叠该房间不可选。status IN (0,1,2)表示待确认、已确认、已入住的订单都算占用已完成和已取消的不算。这个查询在并发下仍然可能出问题后面避坑章节会讲怎么用数据库唯一约束或乐观锁兜底。3. Servlet 与 JSP 怎么配合从请求到页面的完整链路3.1 一个下单请求的完整流转用户在前台页面点击“提交预订”浏览器发一个 POST 请求到/order/create。这个请求先经过编码过滤器把字符集设为 UTF-8然后进入OrderCreateServlet。Servlet 里做四件事从 session 取当前用户、读取表单参数、调用 Service 层创建订单、根据结果跳转到订单详情页或错误页。Service 层在创建订单前会再查一次房态确认该房间在所选日期内没有被占用然后计算总价并写入orders表。写入成功后返回订单对象Servlet 把订单放进 request 作用域转发到front/order_detail.jsp。整个链路里Servlet 不直接拼 HTMLJSP 也不写业务逻辑。这是 MVC 的基本要求但很多人写着写着就把 SQL 塞进 JSP 的% %里了后面改起来非常痛苦。下面是一个精简后的 Servlet 代码WebServlet(/order/create) public class OrderCreateServlet extends HttpServlet { private OrderService orderService new OrderService(); Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 1. 校验登录 User user (User) req.getSession().getAttribute(loginUser); if (user null) { resp.sendRedirect(req.getContextPath() /front/login.jsp); return; } // 2. 读取参数 int roomId Integer.parseInt(req.getParameter(roomId)); LocalDate checkIn LocalDate.parse(req.getParameter(checkIn)); LocalDate checkOut LocalDate.parse(req.getParameter(checkOut)); // 3. 调用业务层 try { Order order orderService.createOrder(user.getId(), roomId, checkIn, checkOut); req.setAttribute(order, order); req.getRequestDispatcher(/front/order_detail.jsp).forward(req, resp); } catch (BizException e) { req.setAttribute(errorMsg, e.getMessage()); req.getRequestDispatcher(/front/error.jsp).forward(req, resp); } } }参数说明roomId来自页面隐藏域或链接参数checkIn和checkOut用yyyy-MM-dd格式字符串传入LocalDate.parse默认就认这个格式。BizException是自定义业务异常比如“房间已被预订”就抛这个Servlet 捕获后转发到错误页展示提示。3.2 后台房态管理的 Servlet 映射后台的房态管理需要支持按房间号筛选、批量修改状态。我一般用一个AdminRoomServlet处理/admin/room/*下的请求通过pathInfo区分具体操作WebServlet(/admin/room/*) public class AdminRoomServlet extends HttpServlet { private RoomService roomService new RoomService(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path req.getPathInfo(); // 例如 /list 或 /edit if (/list.equals(path)) { String roomNo req.getParameter(roomNo); ListRoom rooms roomService.findRooms(roomNo); req.setAttribute(rooms, rooms); req.getRequestDispatcher(/admin/room_list.jsp).forward(req, resp); } else if (/edit.equals(path)) { int id Integer.parseInt(req.getParameter(id)); Room room roomService.findById(id); req.setAttribute(room, room); req.getRequestDispatcher(/admin/room_edit.jsp).forward(req, resp); } } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { int id Integer.parseInt(req.getParameter(id)); int status Integer.parseInt(req.getParameter(status)); roomService.updateStatus(id, status); resp.sendRedirect(req.getContextPath() /admin/room/list); } }这种用pathInfo分发的写法比给每个操作单独建一个 Servlet 要清爽适合后台这种操作集中的场景。注意doPost里改完状态后用sendRedirect而不是forward避免用户刷新页面时重复提交。3.3 JSP 页面里怎么安全地展示数据JSP 里展示数据第一原则是不要用脚本片段写 Java 逻辑。用 JSTL 和 EL 表达式页面干净且不容易出空指针。房型列表页的核心片段% taglib prefixc urihttp://java.sun.com/jsp/jstl/core % table trth房间号/thth房型/thth价格/thth操作/th/tr c:forEach items${rooms} varroom tr td${room.roomNo}/td td${room.typeName}/td td${room.price}/td td a href${pageContext.request.contextPath}/order/toCreate?roomId${room.id}预订/a /td /tr /c:forEach /table${pageContext.request.contextPath}用来拼上下文路径避免部署路径变化后链接失效。c:forEach遍历 request 作用域里的rooms集合这个集合是 Servlet 转发前setAttribute放进去的。如果要在 JSP 里显示当前登录用户直接从 session 取c:if test${not empty sessionScope.loginUser} 欢迎${sessionScope.loginUser.username} /c:if注意sessionScope前缀不能省否则 EL 会按 page、request、session、application 的顺序去找虽然也能找到但显式写出来更清晰。3.4 权限过滤器怎么写才不漏后台页面必须做管理员校验。用 Filter 拦截/admin/*检查 session 里的用户角色WebFilter(/admin/*) public class AdminAuthFilter implements Filter { Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; User user (User) request.getSession().getAttribute(loginUser); if (user null || user.getRole() ! 1) { response.sendRedirect(request.getContextPath() /front/login.jsp); return; } chain.doFilter(req, resp); } }这里有个细节登录页本身如果放在/admin/下会被这个过滤器拦住导致死循环。所以登录页要么放前台目录要么在过滤器里放行/admin/login路径。我一般把管理员登录页放在/admin/login.jsp然后在过滤器里加一个判断如果请求路径以/admin/login开头就直接放行。4. 避坑与排查那些让我加班到凌晨的细节4.1 中文乱码POST 和 GET 要分开处理现象前台表单提交后数据库里存进去的中文变成问号或乱码。原因POST 请求的编码要在读取参数之前设置GET 请求在 Tomcat 8 以后默认 URI 编码是 UTF-8但旧版本或配置不对时仍会乱码。解决加一个编码过滤器对所有请求设置request.setCharacterEncoding(UTF-8)和response.setContentType(text/html;charsetUTF-8)。注意setCharacterEncoding必须在任何getParameter调用之前执行否则无效。GET 请求如果还有问题检查 Tomcat 的server.xml里 Connector 的URIEncoding是否为 UTF-8。4.2 订单超卖两个用户同时订同一间房现象两个用户几乎同时提交同一间房的订单系统都提示成功但房间只有一间。原因Service 层先查房态再插入订单这两步之间有时间窗口并发时两个线程都查到房间空闲。解决在orders表上加一个唯一索引兜底比如对room_id和check_in组合加唯一约束或者用数据库的SELECT ... FOR UPDATE在事务里锁住房记录。更简单的做法是在room表加一个版本号字段更新时用UPDATE room SET status1, versionversion1 WHERE id? AND version?根据影响行数判断是否成功。我一般先用唯一索引快速兜底后面再优化成乐观锁。4.3 JSP 页面路径写死导致部署后 404现象本地跑得好好的换一个上下文路径部署后所有链接和静态资源都 404。原因JSP 里用了/front/room_list.jsp这种以斜杠开头的绝对路径没有拼上下文路径。解决所有链接和资源引用都用${pageContext.request.contextPath}开头。表单的 action 也一样。如果用了 JSTL 的c:url标签它会自动拼上下文路径更省心。4.4 后台修改房态后前台不更新现象管理员在后台把房间状态改成“维护”但前台房型列表里这间房还能被预订。原因前台查询可用房间的 SQL 只判断了订单占用没有判断room.status。或者前台页面被浏览器缓存了。解决前台查询里必须加上r.status 0的条件。另外在 Servlet 里设置响应头禁止缓存response.setHeader(Cache-Control, no-cache, no-store, must-revalidate)。后台修改状态后前台下次查询自然会拿到最新数据。4.5 日期格式转换异常现象用户输入的日期格式不是yyyy-MM-dd比如2024/01/01LocalDate.parse直接抛异常页面报 500。原因LocalDate.parse默认只认 ISO 格式。解决在 Servlet 里用DateTimeFormatter.ofPattern(yyyy-MM-dd)显式指定格式并且用 try-catch 包住解析过程解析失败时给用户一个友好提示而不是 500 错误页。前端也可以用input typedate限制输入格式但后端校验不能省。5. 进阶技巧用 JSTL 自定义标签和连接池把项目收干净项目能跑起来之后真正拉开差距的是代码的整洁度和可维护性。这里分享两个我每次都会用的收尾技巧。第一个是用 JSTL 自定义标签处理状态显示。订单状态在数据库里是 0、1、2、3、4直接显示数字用户看不懂。写一个简单的标签类public class OrderStatusTag extends SimpleTagSupport { private int status; public void setStatus(int status) { this.status status; } Override public void doTag() throws JspException, IOException { String[] texts {待确认, 已确认, 已入住, 已完成, 已取消}; getJspContext().getOut().write(texts[status]); } }然后在 JSP 里用h:orderStatus status${order.status} /就能显示中文状态。标签描述文件放在WEB-INF下不用改 web.xml。这样做的好处是状态文案集中在一处以后要改只改标签类。第二个是数据库连接池。很多课程设计直接用DriverManager.getConnection每次请求都新建连接并发一上来就崩。换成 Druid 或 HikariCP 只需要加一个配置文件和一个工具类public class DBUtil { private static DataSource dataSource; static { try { Properties props new Properties(); props.load(DBUtil.class.getClassLoader().getResourceAsStream(db.properties)); dataSource DruidDataSourceFactory.createDataSource(props); } catch (Exception e) { throw new ExceptionInInitializerError(e); } } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } }db.properties里配好 url、username、password、initialSize、maxActive 就行。DAO 里统一用DBUtil.getConnection()关闭时用 try-with-resources 自动归还连接。这一步做完项目才算从“能跑”变成“敢给人看”。最后说一个验证方法把项目部署到 Tomcat 后用浏览器的无痕模式分别走一遍普通用户下单流程和管理员审核流程重点看 session 是否在关闭浏览器后失效、后台路径是否在未登录时被正确拦截、订单金额是否和房型价格乘以天数一致。这三个点过了基本就没有大问题。我自己踩过最深的坑是早期把价格计算放在 JSP 里做结果改价后历史订单金额全变了被用户投诉才发现。从那以后我坚持一个习惯凡是和钱、时间、状态相关的计算一律放在 Service 层并且订单生成时把价格固化到订单表。这个习惯帮我省掉了无数次后悔药。希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号