SSM+JSP酒店客房预定系统:从架构到并发控制的毕业设计实战
简介这套Java毕业设计资源围绕酒店客房预定管理系统展开基于SSM框架与JSP技术适合计算机相关专业本科生或专科生完成毕业设计、课程设计时参考也可作为Java Web开发入门者的练习项目。系统按管理员和用户两类角色划分管理员可处理个人中心、用户管理、客房类型、酒店客房、客房预定、开房记录、退房结账及系统公告等业务用户则能浏览首页、在线查看客房、管理个人中心整体业务链完整。资源包共1253个文件以Java源码、JSP页面、CSS样式、JavaScript脚本为主包含SQL建库脚本与说明文档压缩包大小18.15MB目录结构清晰便于按模块查阅。附带的说明文档和LW文档可辅助撰写毕业论文PPT可配合答辩演示帮助读者快速理解设计思路与实现细节。目前已有208人学习下载适合需要快速搭建同类型信息管理系统的开发者借鉴复用。1. 酒店客房预定管理系统毕业设计里最被低估的「稳」选题每年毕业设计季Java Web 方向的选题池里总有几个高频面孔电商、论坛、博客还有酒店客房预定管理系统。很多人嫌弃它太普通扭头去选更有排面的结果需求越滚越大答辩前一周还在补 Bug。这个选题是典型的小而不薄业务链条完整每一环又不至于失控。所谓 SSM JSP就是 Spring、SpringMVC、MyBatis 加 JSP 视图的经典组合恰好配套完整源码、说明文档、LW 论文和答辩 PPT 四件套。客户选房、下单预定、校验可订、生成订单、入住退房、统计营业额这套闭环正好覆盖状态流转、日期冲突和聚合统计这些答辩高频考点。照抄代码能过查重过不了答辩提问这篇按一线做法把系统拆通透。2. SSM JSP 架构拆解这套「老组合」为什么还是毕业设计主力2.1 三层架构与 MVC 的对应关系先分清谁干什么拿到压缩包的第一件事不是急着解压跑代码而是先看懂分层。SSM 项目本质上是经典三层架构加 MVC 思想的结合很多同学答辩时被问你的项目架构是什么就卡住是因为从来没把这两套概念对到自己的代码上。表现层由 JSP 页面和 SpringMVC 的 Controller 共同构成。JSP 负责渲染数据和收集输入Controller 负责接收请求参数、调用业务层、决定页面跳转对应 MVC 里的 View 和 Controller。业务层是 Service 接口加实现类处理预定时的时间冲突判断、订单状态流转、价格计算这类规则。持久层是 MyBatis 的 Mapper 接口和 XML 映射文件只负责和 MySQL 打交道。层次技术组件核心职责MVC 对应表现层JSP、Controller参数接收、页面跳转、简单校验View Controller业务层Service 接口 实现业务规则、事务边界Model持久层Mapper 接口 XMLSQL 执行、结果映射Model我见过太多人在 Service 里写 SQL、在 Controller 里堆业务判断项目能跑但被问一句订单状态要加一种你得改几个文件就答不上来。分层清晰的项目加状态只需要改数据库字典和 Service 判断其他层几乎不动。这也是为什么我拿到任何 SSM 源码第一步永远是画目录树和分层图而不是直接启动 Tomcat。2.2 拿到源码第一件事把这个目录结构印进脑子里一个标准的 SSM JSP 酒店客房预定系统目录骨架八九不离十是这样包名可能不同职责划分是同一套hotel-ssm/ ├── pom.xml ├── sql/ │ └── hotel_db.sql ├── src/main/java/ │ ├── com/hotel/controller/ # Controller 层 │ │ ├── RoomController.java │ │ ├── ReservationController.java │ │ └── CustomerController.java │ ├── com/hotel/service/ # 业务层接口与实现 │ │ ├── ReservationService.java │ │ └── impl/ReservationServiceImpl.java │ ├── com/hotel/mapper/ # MyBatis Mapper 接口 │ │ ├── RoomMapper.java │ │ └── ReservationMapper.java │ ├── com/hotel/entity/ # 实体类 │ │ ├── Room.java │ │ ├── Reservation.java │ │ └── Customer.java │ └── com/hotel/util/ # 工具类 ├── src/main/resources/ │ ├── jdbc.properties │ ├── spring-mybatis.xml │ ├── spring-mvc.xml │ └── mapper/ # MyBatis XML 映射 │ ├── RoomMapper.xml │ └── ReservationMapper.xml └── src/main/webapp/ ├── WEB-INF/ │ ├── web.xml │ └── views/ # JSP 页面 │ ├── index.jsp │ ├── room/ │ └── reservation/ ├── static/ # css / js / 图片 └── uploads/ # 房间图片上传目录两个新手最容易忽略的点。第一src/main/resources/mapper/下的 XML 和com/hotel/mapper/下的接口是成对出现的MyBatis 要求 XML 的 namespace 等于接口全限定名否则启动后第一次调用就报BindingException。第二WEB-INF/views/下的 JSP 不能通过 URL 直接访问必须由 Controller 转发进入这是安全设计答辩时主动讲出来是加分项。sql/hotel_db.sql是整个系统的地基。我的习惯是拿到项目先执行脚本建库再回头看代码因为很多业务逻辑不看表结构根本读不懂。比如订单状态字段是用TINYINT存数字还是VARCHAR存字符串直接决定 Service 层的写法。2.3 SSM 整合的关键三份配置文件各管一摊SSM 整合的核心是搞清楚 Spring 根容器和 SpringMVC 子容器谁管谁。常见配置是两份 Spring XML 加一份web.xmlspring-mybatis.xml管数据源、事务、Mapper 扫描和 Servicespring-mvc.xml管 Controller 扫描、视图解析器和静态资源放行。!-- 1. 读取数据库连接配置 -- context:property-placeholder locationclasspath:jdbc.properties/ !-- 2. 数据源 -- bean iddataSource classorg.springframework.jdbc.datasource.DriverManagerDataSource property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ /bean !-- 3. SqlSessionFactory注册 Mapper XML 位置 -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean !-- 4. 扫描 Mapper 接口生成代理 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.hotel.mapper/ /bean !-- 5. 事务管理器与注解事务开关 -- bean iddataSourceTransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managerdataSourceTransactionManager/ !-- 6. 扫描 Service放入根容器 -- context:component-scan base-packagecom.hotel.service/参数说明mapperLocations决定 MyBatis 去哪个目录找 XML路径写错启动不报错第一次调用 Mapper 方法才抛Invalid bound statement属于延迟爆雷。MapperScannerConfigurer扫描接口包把接口和 XML 通过 namespace 绑定后生成代理对象注入到 Service 里。tx:annotation-driven是事务总开关漏了它Service 上的Transactional全部静默失效后面专门讲这个坑。再看spring-mvc.xml!-- 只扫 Controller不要扫到 Service -- context:component-scan base-packagecom.hotel.controller/ !-- 视图解析器返回的字符串拼成 /WEB-INF/views/xxx.jsp -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/views// property namesuffix value.jsp/ /bean !-- 注解驱动RequestMapping、JSON 转换 -- mvc:annotation-driven/ !-- 静态资源放行css/js/图片不经过 Controller -- mvc:resources mapping/static/** location/static//这里最容易踩的雷是扫描范围串了spring-mybatis.xml扫 Servicespring-mvc.xml只扫 Controller。如果 Controller 扫描范围误扫了 Service会出现事务失效、代理对象双份这类怪问题。确认容器边界是 SSM 排错的第一步。最后web.xml里挂上监听器和 DispatcherServletlistener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mybatis.xml/param-value /context-param servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-mvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping逻辑说明ContextLoaderListener创建 Spring 根容器加载spring-mybatis.xmlDispatcherServlet创建 SpringMVC 子容器加载spring-mvc.xml。子容器能看到父容器的 Bean反之不行。答辩时说出SpringMVC 容器是 Spring 容器的子容器Controller 能注入 Service这层关系比背十句框架特性都管用。提示Tomcat 启动报ClassNotFoundException优先查WEB-INF/lib缺不缺 jar。用 Maven 就执行mvn clean package打 war 再部署能躲掉一堆依赖缺失的玄学问题。3. 数据库设计房间、订单、会员三张核心表先把状态想清楚3.1 核心表结构状态字段决定业务复杂度酒店客房预定管理系统的数据库设计重点不在表的数量而在状态字段怎么定义。最小闭环需要房间、客户、订单三张表。房间有空闲、已预订、已入住、维修四种状态订单有待确认、已确认、已入住、已退房、已取消五种状态两组状态的联动就是整个系统最核心的业务规则。状态值房间状态业务含义0空闲可预订、可入住1已预订有生效订单不可再订2已入住客人住店中3维修人工置为不可用CREATE TABLE room ( room_id INT NOT NULL AUTO_INCREMENT, room_number VARCHAR(20) NOT NULL COMMENT 房间号如 801, room_type VARCHAR(20) DEFAULT 单人间 COMMENT 单人间/双人间/套房, price DECIMAL(10,2) NOT NULL COMMENT 门市价, status TINYINT NOT NULL DEFAULT 0 COMMENT 0空闲 1已预订 2已入住 3维修, floor INT DEFAULT NULL COMMENT 楼层用于筛选, description VARCHAR(255) DEFAULT NULL, PRIMARY KEY (room_id), UNIQUE KEY uk_room_number (room_number) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE customer ( customer_id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 登录名, password VARCHAR(100) NOT NULL COMMENT 存哈希别存明文, phone VARCHAR(20) DEFAULT NULL, id_card VARCHAR(18) DEFAULT NULL COMMENT 入住登记用, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (customer_id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明status用TINYINT存数字比VARCHAR存中文更省空间、查询更快代价是可读性差所以每条都写COMMENT注释说明枚举含义。房间号加唯一索引防止并发下重复插入同一房间。密码至少要存 MD5答辩被问密码怎么存的答明文是硬伤。说毕业设计用 MD5生产环境会换 BCrypt 加盐已经强过大多数同学。3.2 订单表与预定冲突判断整个系统最核心的一段 SQL订单表连接客户和房间。设计上要冗余房间号和一个价格快照理由很实在房间价格会调整但订单必须保留下单那一刻的价格这个细节答辩时讲出来就是亮点。CREATE TABLE reservation ( id INT NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单号程序生成, customer_id INT NOT NULL, room_id INT NOT NULL, room_number VARCHAR(20) DEFAULT NULL COMMENT 冗余房间号, room_type VARCHAR(20) DEFAULT NULL COMMENT 冗余房型, price DECIMAL(10,2) NOT NULL COMMENT 房价快照, check_in_date DATE NOT NULL COMMENT 入住日期, check_out_date DATE NOT NULL COMMENT 离店日期, total_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, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_room_date (room_id, check_in_date, check_out_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;预定系统的核心是判断一个房间在日期区间内是否被占用。两个区间没有重叠的条件是新入住 已有离店或新离店 已有入住取反就是冲突条件写成 SQL 是经典的四行判断-- 判断某房间在 [checkIn, checkOut) 区间内是否已有生效订单 SELECT COUNT(*) FROM reservation WHERE room_id #{roomId} AND status IN (1, 2) -- 已确认、已入住才算占用 AND #{checkInDate} lt; check_out_date AND #{checkOutDate} gt; check_in_date逻辑说明状态过滤是关键。待确认和已取消不占用房间只有已确认和已入住才算数。漏掉这个条件会出现待支付订单占着房别人订不了的尴尬。比较符号在 MyBatis XML 里必须写成lt;和gt;直接写会被 XML 解析器当成标签开头这是新手最容易卡住的低级坑。区间用左闭右开语义当天入住、当天离店视为不占用方便处理连住场景。3.3 管理端报表用一条 GROUP BY 撑起营业额统计管理端统计报表答辩必问。常见需求是按月统计营业额和按房型统计入住率。营业额统计的 SQL 不复杂但要选对状态——只有已退房的订单才算完成收款已确认未入住的不能算进营收SELECT DATE_FORMAT(check_out_date, %Y-%m) AS month, COUNT(*) AS order_count, SUM(total_price) AS revenue FROM reservation WHERE status 3 -- 已退房订单完成 AND check_out_date BETWEEN #{startDate} AND #{endDate} GROUP BY DATE_FORMAT(check_out_date, %Y-%m) ORDER BY month DESC;参数说明DATE_FORMAT(check_out_date, %Y-%m)把日期格式化成年月用于分组。用check_out_date作为统计归属时间比用create_time更符合酒店财务口径只有实际离店才产生收入。这个细节讲出来评委能看出你思考过业务而不是只会调 ORM。状态3必须和你表里的定义一致。很多人把订单状态顺序打乱报表数据不对时先回去看建表语句的注释别急着改代码。入住率统计口径建议提前想好分子用已入住的DATEDIFF(check_out_date, check_in_date)求和分母用房间数乘统计天数。评委很爱追问统计口径。4. 从 JSP 页面到数据库一次提交预订请求的完整生命周期4.1 Controller 层参数绑定与页面转发这条主线把一次预订请求从浏览器走到底层数据库。用户在 JSP 页面填好入住和离店日期、选房间、点提交请求先到ReservationControllerController RequestMapping(/reservation) public class ReservationController { Autowired private ReservationService reservationService; RequestMapping(value /submit, method RequestMethod.POST) public String submit(Reservation reservation, Model model, HttpSession session) { // 从登录态取当前用户没登录直接踢回登录页 Customer customer (Customer) session.getAttribute(loginUser); if (customer null) { return redirect:/login.jsp; } reservation.setCustomerId(customer.getCustomerId()); // 业务层返回自定义结果对象带回提示信息 Result bizResult reservationService.createReservation(reservation); model.addAttribute(bizResult, bizResult); if (bizResult.isSuccess()) { return reservation/success; } // 失败回填空表单保留用户已填内容 model.addAttribute(reservation, reservation); return reservation/book; } }逻辑说明SpringMVC 把表单里的name属性自动绑定到Reservation实体同名属性例如input nameroomId提交后reservation.getRoomId()直接有值省掉手动request.getParameter。Model.addAttribute往 JSP 传数据页面用${bizResult.message}读取。redirect:前缀发起新请求URL 会变不带前缀的reservation/success是服务端转发URL 不变。这个区别也是高频率面试题答辩前过一遍。4.2 Service 层业务规则集中在这一层业务层是答辩提问的重灾区也是代码质量分水岭。下单的 Service 至少做三件事校验日期合法性、校验房间冲突、在事务里完成订单插入和房间状态更新。Service Transactional public class ReservationServiceImpl implements ReservationService { Autowired private ReservationMapper reservationMapper; Autowired private RoomMapper roomMapper; Override public Result createReservation(Reservation r) { // 1. 入参校验日期不能倒挂入住不能早于今天 LocalDate checkIn r.getCheckInDate(); LocalDate checkOut r.getCheckOutDate(); if (checkIn null || checkOut null || !checkIn.isBefore(checkOut)) { return Result.fail(入住日期必须早于离店日期); } if (checkIn.isBefore(LocalDate.now())) { return Result.fail(入住日期不能早于今天); } // 2. 核心冲突校验查重叠订单数 int overlapCount reservationMapper.countOverlap( r.getRoomId(), r.getCheckInDate(), r.getCheckOutDate()); if (overlapCount 0) { return Result.fail(该房间在所选日期内已被预定); } // 3. 组装订单生成订单号、计算总价价格必须服务端算 Room room roomMapper.selectById(r.getRoomId()); if (room null || room.getStatus() 3) { return Result.fail(房间不存在或处于维修状态); } r.setOrderNo(generateOrderNo()); r.setRoomNumber(room.getRoomNumber()); r.setRoomType(room.getRoomType()); r.setPrice(room.getPrice()); long days ChronoUnit.DAYS.between(checkIn, checkOut); r.setTotalPrice(room.getPrice().multiply(BigDecimal.valueOf(days))); // 4. 插入订单 更新房间状态同一事务要么都成要么都回滚 reservationMapper.insert(r); roomMapper.updateStatus(r.getRoomId(), 1); return Result.success(预订成功, r); } private String generateOrderNo() { // 订单号规则yyyyMMddHHmmss 4位随机数 return new SimpleDateFormat(yyyyMMddHHmmss).format(new Date()) String.format(%04d, (int) (Math.random() * 10000)); } }参数说明Transactional挂在类上类里 public 方法默认开启事务。第 4 步的insert和updateStatus要么都成功要么都回滚不会出现订单插入成功但房间状态没改的中间态。日期用java.time.LocalDateisBefore直接比较。注意老版本 MySQL 驱动5.1.37 之前不能直接绑定LocalDate报类型转换错误的话把实体字段换成java.sql.Date或字符串就能绕过去。BigDecimal算钱double 算钱有精度问题这个点答辩时主动提是加分项。总价、状态、订单号全部由服务端决定绝不从前端接收防止被抓包篡改。4.3 Mapper 层与 MyBatis XML动态 SQL 和参数绑定Service 里调的countOverlap和insertSQL 写在ReservationMapper.xml里。SQL 和 Java 分开是 MyBatis 的设计特点改 SQL 不用重新编译也方便 DBA 审查。先看接口多参数必须加Param才能绑定public interface ReservationMapper { // 多参数必须写 ParamXML 里才能用 #{roomId} 这种占位符 int countOverlap(Param(roomId) Integer roomId, Param(checkInDate) LocalDate checkInDate, Param(checkOutDate) LocalDate checkOutDate); int insert(Reservation reservation); }select idcountOverlap resultTypeint SELECT COUNT(*) FROM reservation WHERE room_id #{roomId} AND status IN (1, 2) AND #{checkInDate} lt; check_out_date AND #{checkOutDate} gt; check_in_date /select insert idinsert parameterTypecom.hotel.entity.Reservation useGeneratedKeystrue keyPropertyid INSERT INTO reservation (order_no, customer_id, room_id, room_number, room_type, price, check_in_date, check_out_date, total_price, status) VALUES (#{orderNo}, #{customerId}, #{roomId}, #{roomNumber}, #{roomType}, #{price}, #{checkInDate}, #{checkOutDate}, #{totalPrice}, 0) /insert逻辑说明#{}是预编译占位符MyBatis 用?代替并设置参数能防 SQL 注入。这是必考题你要答出#{}走PreparedStatement${}是字符串拼接有注入风险。useGeneratedKeystrue配合keyPropertyid插入后自增主键回填到实体id属性后续要拿订单 ID 关联其他表就方便。插入时status写死0total_price由服务端计算前端只传日期和房间这是安全习惯——输入不可信。4.4 JSP 页面与表单回显EL 表达式和 JSTL 的配合视图层用 JSP核心是 EL 表达式${}和 JSTL 标签。预订表单页面长这样% page contentTypetext/html;charsetUTF-8 languagejava % % taglib prefixc urihttp://java.sun.com/jsp/jstl/core % html body h3预订房间 - ${room.roomNumber}/h3 form action${pageContext.request.contextPath}/reservation/submit methodpost input typehidden nameroomId value${room.roomId}/ 入住日期input typedate namecheckInDate value${reservation.checkInDate}/ 离店日期input typedate namecheckOutDate value${reservation.checkOutDate}/ 房价span${room.price} 元/晚/span button typesubmit提交预订/button /form c:if test${not empty bizResult} p stylecolor:${bizResult.success ? green : red}${bizResult.message}/p /c:if /body /html参数说明${pageContext.request.contextPath}动态取项目上下文路径避免部署路径变化导致表单 404。硬编码成/hotel-ssm/...换台机器或改 war 包名就废了。EL 取值本质是调用实体 getter所以实体属性命名要规范getter/setter 必须齐全。${bizResult.success ? green : red}三元表达式和c:if配合做条件渲染这是 JSP 页面最常见的组合写法。提示JSP 报 500 时先看 Tomcat 日志第一行异常堆栈绝大多数是 null 指针来源往往是 Controller 没往 Model 放数据页面就开始取。5. SSM JSP 项目高发坑排查乱码、404、事务失效5 个必踩的雷5.1 数据库中文乱码从连接 URL 到建表语句三层都要对齐现象页面上中文正常存进数据库变成???或者数据库里是中文页面显示乱码。原因字符集不一致。MySQL 表用了utf8mb4但 JDBC 连接 URL 没指定编码连接层用了默认的latin1中文在传输中被转成问号。解决三层全部对齐。建库建表统一utf8mb4JSP 页面顶部设置contentTypetext/html;charsetUTF-8最重要的是连接 URL 追加参数jdbc.urljdbc:mysql://localhost:3306/hotel_db?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai排查顺序先SHOW CREATE TABLE room看表字符集再看连接 URL最后确认请求编码过滤器在DispatcherServlet之前注册三步基本定位。5.2 静态资源 404spring-mvc.xml 里漏了放行现象页面 HTML 结构正常但 CSS、图片、JS 全部加载失败F12 控制台一堆 404。原因DispatcherServlet的url-pattern//url-pattern拦了所有请求包括/static/css/style.cssSpringMVC 默认不认这些静态资源。解决在spring-mvc.xml里显式放行mvc:resources mapping/static/** location/static//同时 JSP 里的引用写成${pageContext.request.contextPath}/static/css/style.css。注意location结尾的斜杠不能丢/static/和/static行为不同这种细节最耗时间。5.3 事务注解不生效三个原因按概率排序现象Service 里第一步插入订单成功第二步更新房间状态抛异常数据库里订单居然还在回滚没发生。原因按概率排序spring-mybatis.xml里没写tx:annotation-driven注解被当空气。Transactional加在非 public 方法上Spring 的代理只对 public 生效。同类内部通过this调用事务方法调用走的是原始对象不是代理拦截器被绕过。解决最常见的是第三种。把事务方法拆到独立 Bean或者把两个数据库操作放同一个 public 方法里让外部调用走代理。答辩时说出Spring 事务基于 AOP 代理内部自调用不走代理所以失效这句已经碾压九成同学。5.4 Mapper 接口与 XML 绑定失败Invalid bound statement现象Tomcat 启动正常一调用 Mapper 方法就抛org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.hotel.mapper.RoomMapper.selectById。原因接口和 XML 没绑上。要么mapperLocations路径和 XML 实际位置对不上要么 XML 的namespace没写成接口全限定名要么select的id和方法名不一致。解决按三个一致逐项核对——文件路径一致、namespace 一致、方法名和id一致。如果是 Maven 项目还要确认打包时src/main/resources/mapper/*.xml被打进WEB-INF/classes/mapper/文件不在就是打包配置问题。5.5 依赖版本冲突Tomcat 启动时 NoClassDefFoundError现象本地开发工具里跑得好好的换台电脑或部署到服务器就报NoClassDefFoundError或LinkageError尤其集中在javax.servlet和jackson相关类。原因Servlet API 依赖的 scope 写错了。默认compile会把javax.servlet-api打进 war 包和 Tomcat 自带的同路径类冲突。这类问题最玄学报错位置千奇百怪根因就那么一个。解决把 Servlet 相关依赖的 scope 改成provideddependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version3.1.0/version scopeprovided/scope /dependencyprovided的含义是编译需要、运行不打进包交给 Tomcat 容器提供。顺手在pom.xml的properties里固定 Java 编译版本为 1.8避免换机器后默认 JDK 不同导致的编译兼容报错。6. 答辩前的硬核验证并发订同一间房系统扛得住吗验证健壮性最狠的一招是并发订同一间房。操作很简单开两个无痕窗口登录两个不同账号同一秒提交同一房间、同一日期的订单。如果系统只是先查重再插入两个并发请求会同时查到无冲突然后双双插入成功这就是并发覆盖也是评委最爱问的高并发场景怎么办。解决办法首选悲观锁把冲突判断整个放进事务先锁房间那一行再查重叠订单Transactional public Result createReservation(Reservation r) { // 先锁住这一行其他并发事务的查询会阻塞在 FOR UPDATE 上 Room room roomMapper.selectByIdForUpdate(r.getRoomId()); int overlap reservationMapper.countOverlap( r.getRoomId(), r.getCheckInDate(), r.getCheckOutDate()); if (overlap 0) return Result.fail(该房间已被预定); reservationMapper.insert(r); roomMapper.updateStatus(r.getRoomId(), 1); return Result.success(预订成功); }select idselectByIdForUpdate resultTypecom.hotel.entity.Room SELECT * FROM room WHERE room_id #{roomId} FOR UPDATE /select逻辑说明FOR UPDATE是 InnoDB 的行级排他锁一个事务锁住该行后另一个事务执行同样的查询会阻塞到第一个事务提交或回滚。顺序上必须先锁再查重两个并发查询才能串行。锁的生命周期依赖事务边界所以Transactional不能省。另一个方案是建唯一索引挡住完全相同的日期但对半重叠区间无效毕业设计里用FOR UPDATE最好讲。这轮验证做完你还能把隔离级别、乐观锁和悲观锁的区别串一遍。我的习惯是答辩前一周每天完整走一遍主流程——注册、登录、订房、退房、统计每步截图。现场先讲并发这个坑和你的解法再演示主流程评委基本不会再往死里追问。这套方法带人验证过好几回至少省三夜通宵。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

Python OPC UA客户端实战:asyncua协议栈搭建与工业现场避坑指南

Python OPC UA客户端实战:asyncua协议栈搭建与工业现场避坑指南

简介:本资源是基于C#实现的OPC UA客户端完整工程示例,面向工业自动化领域开发者、工控系统集成工程师及物联网协议学习者,解决OPC UA客户端开发入门难、安全连接配置复杂、服务调用逻辑不清晰等实际问题。压缩包共172个文件,含58个…

2026/10/18 22:31:42 阅读更多 →
Claude Code配置实战:从基础环境到MCP与工作流设计

Claude Code配置实战:从基础环境到MCP与工作流设计

1. 装好 Claude Code 之前,先把这几件基础配置搞清楚我大概是今年年初开始重度使用 Claude Code 的。在这之前,我经历了很长一段“AI 辅助编程焦虑期”——ChatGPT 写代码要自己复制粘贴、改一个小需求来回传好几轮,Cline 之类的插件在 IDE 里…

2026/10/18 22:31:42 阅读更多 →
位运算分组思想:异或运算破解“只出现一次的数字 III”

位运算分组思想:异或运算破解“只出现一次的数字 III”

这个题目其实是力扣上“只出现一次的数字”系列的第三题,老粉丝应该一眼就认出来了。前面两道题一个用异或就能秒杀,一个需要位运算加状态机,到了第三题,难度开始上去:整个数组里有两个只出现一次的数字,剩…

2026/10/18 22:31:42 阅读更多 →

最新新闻

YOLO-Master:从数据标注到部署的完整目标检测工程实战指南

YOLO-Master:从数据标注到部署的完整目标检测工程实战指南

第一次跑通 YOLO 目标检测的时候,我的感觉是:模型本身倒没让人崩溃,真正崩溃的是模型外面那一大圈工程问题。数据集该往哪里放、标注格式怎么转、训练到一半显存爆了怎么办、明明 loss 在降结果却一塌糊涂——这些坑会让你在“下载完代码”和…

2026/10/18 23:24:27 阅读更多 →
gdsdecomp 实战指南:5 步把游戏 PCK 恢复成可编辑的 Godot 工程

gdsdecomp 实战指南:5 步把游戏 PCK 恢复成可编辑的 Godot 工程

gdsdecomp 实战指南:5 步把游戏 PCK 恢复成可编辑的 Godot 工程 【免费下载链接】gdsdecomp Godot reverse engineering tools 项目地址: https://gitcode.com/GitHub_Trending/gd/gdsdecomp Godot 游戏已经发布,源码却再也拿不到了?g…

2026/10/18 23:24:27 阅读更多 →
G-Helper 充电限制不生效?三步排查开机任务与电池配置冲突

G-Helper 充电限制不生效?三步排查开机任务与电池配置冲突

G-Helper 充电限制不生效?三步排查开机任务与电池配置冲突 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook…

2026/10/18 23:24:27 阅读更多 →
本地千万级RAG向量库压缩97%:乘积量化与重排实战

本地千万级RAG向量库压缩97%:乘积量化与重排实战

1. 为什么本地跑千万级 RAG 这么难1.1 向量库的“大”到底大在哪做 RAG 的应用基本都绕不开一个问题:embedding 一跑,向量库就越来越肥。很多人第一次算账时会愣住,一千万条常见规格的向量(1536 维,float32&#xff09…

2026/10/18 23:24:27 阅读更多 →
Jupyter快捷键自定义全指南:从Notebook到JupyterLab的高效配置

Jupyter快捷键自定义全指南:从Notebook到JupyterLab的高效配置

1. 搞懂Jupyter的快捷键底子:两种模式才是核心1.1 为什么快捷键要分成“编辑模式”和“命令模式”用Jupyter的人一听到快捷键,第一反应往往是“背不下来”。实际上,Jupyter的快捷键体系并不像Word那样追求“全盘覆盖”,它围绕一个…

2026/10/18 23:23:27 阅读更多 →
Unity 3D框选原理与高性能智能选择系统实现

Unity 3D框选原理与高性能智能选择系统实现

1. 项目概述:为什么“框选多个3D物体”不是个小功能,而是编辑器体验的分水岭在Unity里拖动鼠标画个方框,把场景里散落的十几二十个模型一次性全选中——听起来像基础操作?我带过三届Unity开发实训,每次讲到编辑器扩展&…

2026/10/18 23:23:27 阅读更多 →

日新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/18 0:00:55 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/18 0:00:55 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 0:55:02 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →