
简介面向JavaWeb课程设计或期末作业场景这是一份基于MySQL、IDEA、Tomcat、JSP与Servlet的宾馆管理系统完整源码配套文档说明和代码注释新手也能快速读懂并部署运行。系统涵盖房间管理、预订管理、入住管理、客户管理等典型模块界面简洁、操作流畅适合作为高分课程设计参考。资源包共1370个文件压缩后22.34MB以JSP页面、Java后端类、SQL数据库脚本为核心同时包含CSS、JS、字体等前端资源结构清晰便于对照学习。目前已有375人学习下载经学习者验证可正常使用。下载后只需导入IDEA并配置Tomcat与MySQL简单部署即可运行通过源码注释和文档说明可以清晰理解ServletJSPMVC分层开发思路、数据库表设计以及前后端交互流程对完成JavaWeb大作业或提升开发能力都有直接帮助。1. 期末作业选这套 JavaWeb 技术栈是稳妥不是偷懒如果你正在为「javaweb 期末作业」发愁多半是选了个太新的框架被老师质疑或者太老的方案跑不起来。宾馆管理系统这类 CRUD 项目用 MySQL IDEA Tomcat JSP Servlet 这套组合恰好落在老师熟悉、资料够多、实现不绕的区间里。它不需要你懂 Spring 的 IOC 和 AOP也不要求你理解微服务只要把「数据库连接、页面跳转、表单提交」这三件事做对作业的基本盘就稳了。这套技术栈的关键不是「会不会」而是「每一步能不能在期末前稳定复现」。我把做这个系统时一定会踩的坑、必调的参数、以及让老师愿意多看两眼的交付细节按步骤写给你。2. 在 IDEA 里把 Tomcat MySQL JDK 配到能跑的最小环境版本与三处关键配置2.1 版本选型JDK 8 配 Tomcat 8.5 最省事别追求新很多同学的作业翻车不是代码写得差而是环境版本搭错。Tomcat 8.5 对应 Servlet 3.1 规范支持 JDK 8也兼容 IDEA 里常见的 JSP 语法提示Tomcat 9 对应 Servlet 4.0但它在 IDEA 里跑起来的报错信息更绕比如热词里常出现的「tomcat启动出现」错误多半是版本不匹配导致的。我的建议是JDK 8 Tomcat 8.5 MySQL 5.7 或 8.0。这套组合在你的电脑上跑出来的效果和老师的演示环境最接近可检索的排错资料也最多。IDEA 里配置 Tomcat 时常见做法是在 Run/Debug Configurations 里添加 Tomcat Server - Local然后把 Tomcat 安装目录指到你的解压路径。这里有一个几乎必踩的坑IDEA 社区版Community默认不支持 Tomcat 插件需要手动安装 Smart Tomcat 插件。如果你用的是社区版却发现 New Configuration 里找不到 Tomcat Server这不是你的操作问题是版本功能差异所致装插件就能解决。2.2 三个必调的 IDEA Tomcat 参数配置完 Tomcat 后有三处参数不调好后面写代码时会在浏览器里看到各种莫名其妙的报错。第一处是 Deployment 选项卡把项目 Artifact 添加进去Application context 填/hotel。这个斜杠前不要带项目名否则访问路径会变成http://localhost:8080/hotel_war_exploded/hotel又臭又长。填/hotel之后你的登录页面地址就是http://localhost:8080/hotel/login.jsp清爽且和 Servlet 映射路径拼接起来不容易出错。第二处是 Server 选项卡里的 On frame deactivation 和 HTTP port。On frame deactivation 建议选 Update classes and resources这样改 JSP 文件后切回 IDEA 就能自动同步不用每次手动重启 Tomcat。HTTP port 默认 8080如果 8080 被占用改成 8081/8082 都能跑但全班统一用 8080 的坑在于——一个人开着 Tomcat另一个人就启动失败端口冲突。第三处是 VM options 里加一行 UTF-8 参数-Dfile.encodingUTF-8这行参数解决的是控制台日志乱码问题。不加的话Tomcat 启动日志里偶尔会出现中文乱码干扰你看错误信息。IDE 里设置的 File Encoding 只管 IDE 读文件的编码管不到 JVM 运行时输出所以这个 VM 参数不能省。2.3 MySQL 连接驱动的放置方式别塞进 JDK 目录连接 MySQL 需要 mysql-connector-java 驱动包。很多教程让你把 jar 复制到$JAVA_HOME/lib/ext或$JAVA_HOME/jre/lib/ext这是老办法在新版 JDK 里会有 Access restriction 报错。正确的方式是放进项目的 WEB-INF/lib 目录下IDEA 会自动把 lib 目录下的 jar 打包进 Artifact。驱动版本要和 MySQL 匹配MySQL 5.7 用 5.1.49 或 8.0.x 都行MySQL 8.0 必须用 8.0.x 的驱动包否则连接时报ClassNotFoundException: com.mysql.cj.jdbc.Driver。注意 8.0 驱动的驱动类名变了老代码里写的com.mysql.jdbc.Driver在 8.0 下只剩兼容别名新项目建议直接用com.mysql.cj.jdbc.Driver。数据库连接配置最常写在一个 properties 文件或 DBUtil 工具类里。实践中我习惯把连接信息集中放方便期末交作业时老师在自己的电脑上改一个密码就能连上jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hotel_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8allowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password123456这里每个参数都是踩过坑换来的。serverTimezoneAsia/Shanghai是应对 MySQL 8.0 的时区校验不加会报The server time zone value ??? 倾? is unrecognized。allowPublicKeyRetrievaltrue解决的是 MySQL 8.0 默认认证插件下的Public Key Retrieval is not allowed报错。characterEncodingutf8保证中文写入数据库不乱码。如果你是 5.7 版本这四个参数里前两个不是必须的但加了也无害。3. 把数据库设计成老师看得懂的样子建库脚本、三张核心表与测试数据3.1 为什么宾馆管理系统只需要三张表期末作业的评分重点不是表多而是「业务闭环是否成立」。宾馆管理系统的核心流程是管理员登录 - 房间类型可查 - 房间可预订/入住/退房。我把表拆成三张管理员表、房间表、订单表。房间类型单人间/双人间/套房直接当一个字段放进房间表不需要单独拆表因为期末作业的业务量级根本碰不到 3NF 的坑拆了反而增加联表复杂度你写 JSP 的时候会多绕一层循环。表结构可参考下面的建库脚本CREATE DATABASE IF NOT EXISTS hotel_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hotel_db; CREATE TABLE admin ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 管理员ID, username VARCHAR(20) NOT NULL UNIQUE COMMENT 登录名, password VARCHAR(64) NOT NULL COMMENT 密码建议存MD5 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT管理员表; CREATE TABLE room ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 房间ID, room_no VARCHAR(10) NOT NULL UNIQUE COMMENT 房间号, type VARCHAR(20) NOT NULL COMMENT 房间类型, price DECIMAL(10,2) NOT NULL COMMENT 价格, status TINYINT DEFAULT 0 COMMENT 0空闲 1入住 2打扫中 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT房间表; CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 订单ID, room_id INT NOT NULL COMMENT 房间ID, customer_name VARCHAR(30) NOT NULL COMMENT 顾客姓名, phone VARCHAR(20) COMMENT 联系电话, check_in DATE NOT NULL COMMENT 入住日期, check_out DATE COMMENT 退房日期, amount DECIMAL(10,2) COMMENT 订单金额, status TINYINT DEFAULT 0 COMMENT 0进行中 1已完成 2已取消, FOREIGN KEY (room_id) REFERENCES room(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;建表脚本里有两处故意留的设计取舍。第一密码字段用了 VARCHAR(64)这是给 MD5 留的存储空间不建议明文存密码期末答辩时老师如果问「密码怎么存」你答「MD5 加密后存储」是加分项。第二订单表用room_id外键关联房间而不是冗余一个room_no字符串字段这是让老师看到你懂第二范式的地方。3.2 测试数据让演示现场不尴尬的关键很多作业项目跑起来页面上空空如也老师一点按钮全是「暂无数据」第一印象就扣分了。测试数据要提前造好而且要造得有业务感。管理员表插一条admin/123456房间表插入 8 到 10 条不同价位的房间订单表插入几条状态不同的订单最好含一条「进行中」和一条「已完成」。INSERT INTO admin (username, password) VALUES (admin, MD5(123456)); INSERT INTO room (room_no, type, price, status) VALUES (101, 单人间, 188.00, 0), (102, 单人间, 188.00, 1), (201, 标准间, 268.00, 0), (301, 豪华套房, 688.00, 0); INSERT INTO orders (room_id, customer_name, phone, check_in, check_out, amount, status) VALUES (2, 张三, 13800001111, 2024-06-01, 2024-06-03, 376.00, 0), (4, 李四, 13900002222, 2024-05-20, 2024-05-21, 688.00, 1);测试数据里刻意让房间表status1的那条对应房间里有一条进行中的订单这样在房间列表页上能看到「入住中」的联动效果演示时可讲「状态一致性」。订单金额建议用代码计算而不是手填比如入住天数乘以房价答辩时被追问「金额怎么来的」你能答出逻辑比答不出强得多。3.3 日期类型选 DATE 还是 DATETIME老师说看场景订单表里入住和退房日期我用了 DATE因为宾馆业务不需要精确到时分秒。这里有一个设计细节值得写进文档DATE 类型在 JSP 页面直接用rs.getDate(check_in)取出后格式是2024-06-01不需要额外格式化。如果用 DATETIME取出后会带00:00:00你还得在 JSP 里手动截断字符串多一步就多一个出错位置。如果你想把日期格式统一可以在 SELECT 语句里直接格式化SELECT id, customer_name, DATE_FORMAT(check_in, %Y-%m-%d) AS check_in_str FROM orders;这样可以避免在 JSP 页面写fmt:formatDate标签出错时的额外调试成本。日期处理是期末作业里重灾区我见过太多人在 SimpleDateFormat 上翻车少用一层就没那么多事。4. 用 JSP Servlet 把登录、列表和订房串成一个可交差的闭环从 DBUtil 到页面取值4.1 最小可用的 DBUtil一个类搞定所有连接Servlet 里写 JDBC 代码有一个原则连接管理统一收口不要在十个 Servlet 里重复写十遍Class.forName。DBUtil 类承担两件事加载驱动并创建连接、关闭资源。下面的写法是期末作业里最常见也最不容易错的版本import java.sql.*; public class DBUtil { private static String url jdbc:mysql://localhost:3306/hotel_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingutf8allowPublicKeyRetrievaltrue; private static String user root; private static String password 123456; static { try { Class.forName(com.mysql.cj.jdbc.Driver); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection() throws SQLException { return DriverManager.getConnection(url, user, password); } public static void close(Connection conn, PreparedStatement ps, ResultSet rs) { try { if (rs ! null) rs.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (ps ! null) ps.close(); } catch (SQLException e) { e.printStackTrace(); } try { if (conn ! null) conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }这段代码的关键在close方法三个资源分别判断非空再关顺序是 ResultSet、PreparedStatement、Connection。这是 JDBC 资源释放的标准顺序很多教程只写了conn.close()导致连接池耗尽跑一会儿就报Too many connections。参数说明url 里的参数和 2.3 节 properties 文件保持一致不要一边写配置文件一边在代码里硬编码两套后期改密码会漏改一处。如果你想把密码写在配置文件里而不是硬编码在 java 类中可以用Properties类读取。但期末作业的体量硬编码在 DBUtil 里反而更直观老师打开源码马上能看懂。注意一个字都不要拼错类名DBUtil不要写成DBUtill这个错误我看过不止五遍报ClassNotFoundException的时候全班都在查驱动其实只是多打了一个字母。4.2 登录功能的 Servlet JSP 最小实现先写逻辑后写页面登录功能是宾馆管理系统的门面也是老师第一个点的功能。Servlet 负责接收表单参数、调 DBUtil 查库、决定跳转到哪个页面。JSP 负责展示登录表单和错误提示。先看登录页面的表单部分form actionlogin methodpost input typetext nameusername placeholder用户名 required input typepassword namepassword placeholder密码 required button typesubmit登录/button /form % if (request.getAttribute(error) ! null) { % p stylecolor:red% request.getAttribute(error) %/p % } %表单的 action 写成login而不是login.jsp这是 Servlet 映射的地址。用post方法是因为登录密码不该出现在浏览器 URL 上而且 POST 请求能避免中文乱码的部分问题剩下部分靠过滤器解决。那个% if %判断是 JSP 小脚本输出错误信息的常见写法注意先判断非空再取值否则第一次打开页面时会报空指针。对应的 LoginServlet 代码如下WebServlet(/login) public class LoginServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding(UTF-8); String username request.getParameter(username); String password request.getParameter(password); Connection conn null; PreparedStatement ps null; ResultSet rs null; try { conn DBUtil.getConnection(); String sql SELECT * FROM admin WHERE username? AND passwordMD5(?); ps conn.prepareStatement(sql); ps.setString(1, username); ps.setString(2, password); rs ps.executeQuery(); if (rs.next()) { request.getSession().setAttribute(loginUser, username); response.sendRedirect(roomList); } else { request.setAttribute(error, 用户名或密码错误); request.getRequestDispatcher(login.jsp).forward(request, response); } } catch (SQLException e) { e.printStackTrace(); request.setAttribute(error, 系统异常请稍后重试); request.getRequestDispatcher(login.jsp).forward(request, response); } finally { DBUtil.close(conn, ps, rs); } } }这里用PreparedStatement而不是Statement核心原因有两个预编译防 SQL 注入以及参数用?占位后代码更整洁。MD5(?)让密码在数据库端做摘要比对这样即使数据库泄露原始密码也不会明文暴露。注意request.setCharacterEncoding(UTF-8)必须在读取参数之前调用放到后面就失效了——这是这个项目里最隐蔽的翻车点之一。登录成功后用sendRedirect跳转而不是forward因为 redirect 会让浏览器地址栏变成roomList刷新页面不会重复提交登录表单。用 forward 的话按 F5 会弹出「确认重新提交表单」的提示演示现场会显得很业余。4.3 房间列表和订房功能的 Model 写法与 JSP 取值习惯房间列表页要从数据库查出所有房间并展示。很多人把查询代码直接写在 JSP 里页面开头一大片 Java 代码老师打开源码第一眼就皱眉。正确的分层做法是Servlet 里查数据把结果放到 request 或 session 里JSP 只管遍历展示。Room 实体类照下面这样写字段和表结构一一对应即可public class Room { private int id; private String roomNo; private String type; private double price; private int status; public int getId() { return id; } public void setId(int id) { this.id id; } // roomNo、type、price、status 的 getter/setter 省略 // IDEA 里按 AltInsert 可以自动生成 }RoomListServlet 的核心逻辑只有三行查全部房间、放进 request、转发到 roomList.jsp。这里不贴全代码重点说明 JSP 端遍历取值的一个高频错误。很多同学用forEach标签时写成下面这样导致页面白屏c:forEach items${roomList} varroom tr td${room.roomNo}/td td${room.price}/td /tr /c:forEach如果页面报 500 或者完全不显示先检查两件事页面顶部有没有加% taglib prefixc urihttp://java.sun.com/jsp/jstl/core %以及 WEB-INF/lib 下有没有放 jstl.jar 和 standard.jar。没了 taglib 声明c:forEach会被当成普通文本输出页面不报错但数据就是不出来——这个现象我调试了半小时才发现少了一行。4.4 编码过滤器一次配置全局中文不乱码GET 和 POST 请求的中文乱码机制不同。POST 乱码是因为请求体字节流按 ISO-8859-1 解码GET 乱码是因为 Tomcat 8 之前的默认 URI 解码不是 UTF-8。解决方案是写一个 Filter 统一处理而不是在每个 Servlet 里各写一遍setCharacterEncoding。WebFilter(/*) public class EncodingFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; request.setCharacterEncoding(UTF-8); resp.setCharacterEncoding(UTF-8); resp.setContentType(text/html;charsetUTF-8); chain.doFilter(req, resp); } }/*表示拦截所有请求包括 JSP 和 Servlet。Filter 的生效时机是所有 Servlet 执行之前所以在 Filter 里设置编码能覆盖到全部业务逻辑。resp.setContentType(text/html;charsetUTF-8)不只是给浏览器看还负责告诉 JSP 输出流用什么编码写回响应。加上这个过滤器后代码里所有request.setCharacterEncoding(UTF-8)都可以删掉全局统一由这个入口管。5. Tomcat 部署与联调避坑运行期最常见的 5 个翻车现场5.1 Tomcat 启动闪退控制台连日志都没输出现象点击 IDEA 的运行按钮Tomcat 窗口一闪就消失控制台只看到一行「Disconnected from server」。原因最常见的是端口被占其次是CATALINA_HOME环境变量指向了错误位置。你可以打开命令行手动跑到 Tomcat 的 bin 目录执行catalina run这样错误会直接打在终端上不会闪退。如果看到Port 8080 required by Tomcat v8.5 Server at localhost is already in use说明有进程占用了 8080。解决shell netstat -ano | findstr 8080 taskkill /PID 你的PID /F或者在 IDEA 里把 HTTP port 改成 8081。顺便说一句IDEA 配置页面里的 JMX port 默认 1099也可能被占报 Port already in use: 1099这个端口和 HTTP port 是两个东西改的时候别漏。 ### 5.2 IDEA 2026 版本找不到 Tomcat Server 选项 现象新建 Run Configuration 时列表里压根没有 Tomcat Server 这一项不管新版老版都找不到。 原因你用的是 IDEA Community 版或者 Tomcat 插件没有启用。社区版不内置 Tomcat 集成这个限制从 2017 年左右就存在了不是你的操作问题。 解决装 Smart Tomcat 插件。在 IDEA 的 Plugins 市场搜索 Smart Tomcat安装后在 Run Configuration 里会出现一个 Smart Tomcat 类型的配置指定 Tomcat Server 的路径、Context Path 和部署目录即可。注意 Smart Tomcat 的配置方式和官方 Tomcat 集成不完全一样它的部署路径填的是项目里的 web 目录对应你的项目根路径下那个存放 JSP 和 WEB-INF 的文件夹。 ### 5.3 MySQL 8.0 连接报 Public Key Retrieval is not allowed 现象启动项目后第一次查数据库就抛 SQLException信息里带 Public Key Retrieval is not allowed。这个问题只在 MySQL 8.0 出现5.7 不受影响。 原因MySQL 8.0 默认认证插件是 caching_sha2_password客户端连接时需要先获取服务器的公钥来加密密码传输。JDBC 驱动为了安全默认禁止自动获取公钥需要显式开启。 解决在 JDBC URL 后面追加 allowPublicKeyRetrievaltrueuseSSLfalse。如果加了参数还报错先确认你的驱动 jar 是不是 8.0 版本5.1 的旧驱动连这个参数都不认识。 ### 5.4 JSP 页面中文全部变成问号 现象浏览器里所有中文渲染成 ??? 或者 锟斤拷。这几乎是 JavaWeb 期末作业里最多的乱码形态。 原因三层编码不一致——JSP 文件保存编码不是 UTF-8、JSP 页面声明的 pageEncoding 不对、Tomcat 请求解码默认不是 UTF-8。这三层只有全部对齐才能根治。 解决第一层在 JSP 页面第一行加 % page contentTypetext/html; charsetUTF-8 pageEncodingUTF-8 %第二层确认 IDEA 右下角的文件编码显示为 UTF-8第三层给 Tomcat 的 conf/server.xml 里 Connector 加一行 URIEncodingUTF-8解决 GET 请求的乱码。加上 4.4 节的 Filter四层齐了之后乱码就再也不会出现了。 ### 5.5 登录成功后刷新页面就退出登录 现象登录成功跳转到房间列表但按一下 F5页面就跳回登录页。用户信息是存到 Session 里的Session 还在为什么失效了 原因Session 依赖 Cookie 保存 JSESSIONID。浏览器访问的域名从 localhost:8080 切换到 127.0.0.1:8080 时Cookie 不通用。IDEA 部署后打开的地址有时是 localhost你手动改成 127.0.0.1 再访问Cookie 就丢了。 解决演示时统一用 localhost 或统一用 127.0.0.1不要混着访问。这属于典型的「一会儿能登一会儿不能登」的玄学问题实际是 Cookie 的作用域名不同。期末答辩现场如果老师自己输网址建议你提前把浏览器标签页都打开避免现场敲地址导致 Session 丢失的尴尬。 ## 6. 答辩前的三个加分动作测试数据、启动说明和 30 分钟改需求的能力 期末作业验收时老师第一看跑不跑得起来第二看代码是不是自己写的第三看文档和你口头解释一不一致。前两件事你已经靠前面的步骤解决了第三件我有个习惯在 README 里写一段 200 字的启动顺序从「打开 MySQL 服务 - 导入 hotel_db.sql - 用 IDEA 打开项目 - 配置 Tomcat - 运行」五步讲清楚连密码都标注为 admin/123456。老师在自己的电脑上按顺序操作五分钟内能看到登录页印象分直接在起评线之上。 第二个加分的点是演示前准备好「如果我改一个需求」的底气。答辩时老师大概率会问「如果客人的入住天数变了价格怎么更新」这种开放问题。你不需要现场改代码但可以提前在主页面加一个「退房结算」按钮,逻辑是查出订单对应房间的单价乘以入住天数写入 order 表的 amount 字段。真被问到就翻到 OrdersServlet 里讲这两行 UPDATE 语句用「先查单价再算总额」的流程回答一分不丢。 第三个习惯是截图归档。把登录成功、房间列表、下单成功、退房结算四个页面的截图放进文档的「系统运行截图」章节图片下方用一句话标注功能点。截图不要从浏览器直接截图就贴先按 F12 把浏览器窗口调到合适比例保证页面完整无滚动条。老师看文档时不需要自己跑系统就能了解全貌这会读得快也记得住。 最后说一个折腾这台项目时最值得养成的习惯每完成一个功能把 Tomcat 停掉、重新启动、完整走一遍主流程。JSP 修改后有时 IDEA 没有热部署干净遗留旧 class 文件会导致你怀疑人生这个习惯能救回期末前一晚的睡眠。希望帮到你。 p a hrefhttps://download.csdn.net/download/s44359487yad/90159056 stylecolor:#ec7500;font-size:14px; 本文还有配套的精品资源点击获取 /a img altmenu-r.4af5f7ec.gif srchttps://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif stylewidth:16px;margin-left:4px;vertical-align:text-bottom;cursor:text; /p