ARTICLE DETAIL

资讯详情

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

SSM+MySQL酒店管理系统开发指南:从零搭建到答辩避坑

SSM+MySQL酒店管理系统开发指南:从零搭建到答辩避坑 简介这是一份基于SSMMySQL的酒店管理系统完整项目代码与数据库专为毕业设计、期末大作业和课程设计场景打造也可作为Java Web入门后的综合练习项目。系统覆盖房间管理、预订、入住、订单、用户及评论等核心模块代码带详细注释结构清晰即使Java新手也能快速读懂下载后只需简单配置环境即可部署运行曾被作者作为高分项目获得导师认可。资源包共112个文件以Java源码、XML配置、JSP前端页面和SQL数据库脚本为主辅以properties配置、CSS/JS及少量图片素材压缩包整体约5.94MB便于下载与迁移目录按Controller、Service、Mapper等分层数据库脚本内置基础数据方便快速启动和二次开发。目前已有193人学习适合需要一套可直接参考的SSM酒店管理项目的开发者使用。1. 基于SSMMySQL的酒店管理系统为什么毕业设计总选它自己写一份要多久你解压出“基于SSMMySQL的酒店管理系统项目代码数据库”之后导入IDEA不是缺包就是连不上数据库SQL脚本导进去了登录还是白屏。这不是操作问题是SSM项目在“作者电脑能跑”和“你电脑能跑”之间隔着一堆隐形差异。标题里的SSM指Spring、SpringMVC、MyBatisMySQL负责存储。酒店管理系统成为毕业设计和大作业的常客是因为业务线足够有料房间有类型和状态顾客要预订、入住、退房钱要算管理员要分权限。把这套流程用三层架构表达出来课程设计考察点基本就覆盖了。我按“搭建骨架→设计库表→串主流程→避坑→验收”的顺序把这套系统从零讲到能演示。适合手上有代码跑不起来的人、想重写没头绪的人、怕答辩被问设计理由的人。弯路我已经替你走过。2. 把SSM骨架立起来三层框架怎么协作最小配置该给哪些参数2.1 为什么这个题目死守SSM课程设计需要的是“能解释”的程序这两年新人上手都喜欢Spring Boot因为启动快、配置少跑起来就是一坨自动配置。但很多学校的毕业设计大纲和验收表就是按SSM出的原因也很现实SSM要求你把Spring的Bean管理、SpringMVC的请求分发、MyBatis的SQL映射一层层手动装配起来答辩提问才有东西可讲。换成Spring Boot很多环节自动完成问到底层时容易“黑匣子”。SSM三个框架的分工记住一句话就行Spring是一个容器用IOC管理Service、Mapper这些对象的生命周期用AOP处理事务SpringMVC负责把HTTP请求分发到Controller再返回视图或JSONMyBatis负责把Mapper接口方法翻译成SQL并在XML里写动态SQL。酒店管理系统无非就是“请求进来→调Service→调Mapper→查MySQL→渲染页面”这套分工刚刚好。2.2 标准Maven工程长什么样依赖版本别瞎选先在根目录建一个Maven的war工程不要用jar因为最终要丢进Tomcat。src/main下拆成java、resources、webapp三个目录java里按controller、service、mapper、entity、util分包resources放spring配置、MyBatis配置、jdbc.propertieswebapp放JSP、web.xml和静态资源。很多人拿到项目改半天就是因为Controller包和Mapper包扫描范围对不上类加载到了但代理没生成。pom.xml里的依赖清单是第一个拦路虎。给你一份我用过的组合套餐properties spring.version5.2.22.RELEASE/spring.version mybatis.version3.5.9/mybatis.version /properties dependencies dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-jdbc/artifactId version${spring.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.20/version /dependency dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency dependency groupIdjavax.servlet.jsp/groupId artifactIdjsp-api/artifactId version2.2/version scopeprovided/scope /dependency dependency groupIdjstl/groupId artifactIdjstl/artifactId version1.2/version /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.12.5/version /dependency /dependencies这组版本的前提是你用Tomcat 8或9、JDK 8。凡是依赖里带javax.servlet的都是给Tomcat 9以下准备的如果你装的是Tomcat 10接口变成了jakarta.servletSpring那些旧版本全都不认识DispatcherServlet都起不来。这是我见过最多的“项目跑不了”原因没有之一。2.3 三个配置文件缺一不可数据源、Spring容器、SpringMVC入口先建一个jdbc.properties放在resources根目录这里假设你已经按常规的MySQL安装教程装好8.x版本并建好了库jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/hotel_ssm?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue jdbc.usernameroot jdbc.password你的密码注意几个参数的用途characterEncodingutf8控制中文存储serverTimezone解决MySQL 8时区报错allowPublicKeyRetrievaltrue是专门给MySQL 8的caching_sha2_password插件用的不加会报“Public Key Retrieval is not allowed”这个问题后面避坑章节还会展开。Spring核心配置文件我习惯叫applicationContext.xml骨架是这样context:property-placeholder locationclasspath:jdbc.properties/ bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ property nameinitialSize value5/ property namemaxActive value10/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nametypeAliasesPackage valuecom.xxx.hotel.entity/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.xxx.hotel.mapper/ /bean bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/这里最关键的是MapperScannerConfigurer它把com.xxx.hotel.mapper包下所有接口扫描一遍为每个接口生成一个代理对象Service里才能用Autowired直接注入。typeAliasesPackage的作用是让Mapper XML里的resultType不用写全路径比如只写“RoomType”就够。事务管理器的注入很讲究必须和Service层用同一个数据源否则Transactional锁不住连接。SpringMVC的配置放在spring-mvc.xmlcontext:component-scan base-packagecom.xxx.hotel.controller/ mvc:annotation-driven/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/jsp// property namesuffix value.jsp/ /bean mvc:resources mapping/static/** location/static//controller单独扫描别把service包也扫进来视图解析器指向WEB-INF/jsp好处是用户没法直接通过URL访问JSP源码static放CSS、JS和图片。web.xml里需要注册DispatcherServlet、ContextLoaderListener和CharacterEncodingFilter。CharacterEncodingFilter的配置很重要很多中文乱码就是因为缺了它filter filter-nameencodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param /filter filter-mapping filter-nameencodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping context-param param-namecontextConfigLocation/param-name param-valueclasspath:applicationContext.xml/param-value /context-param listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener 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注意DispatcherServlet的url-pattern是“/”它会把所有请求都交给SpringMVC包括静态资源所以spring-mvc.xml里必须配mvc:resources放行static目录。如果这个顺序错了页面样式会全部404。2.4 跑通第一个列表接口从实体类到JSON返回骨架配完先别急着写业务用一个“查询房间类型”接口验证整套链路是否通。实体类写在entity包里public class RoomType { private Integer id; private String typeName; private Double price; private Integer bedNum; private String remark; // getter / setter }Mapper接口写在mapper包里public interface RoomTypeMapper { ListRoomType listAll(); }对应的XML放在resources/mapper目录下文件名叫RoomTypeMapper.xmlmapper namespacecom.xxx.hotel.mapper.RoomTypeMapper select idlistAll resultTypeRoomType SELECT id, type_name, price, bed_num, remark FROM t_room_type ORDER BY id /select /mapperService层照例写接口和实现Controller再封装一层Controller RequestMapping(/roomType) public class RoomTypeController { Autowired private RoomTypeService roomTypeService; RequestMapping(/list) ResponseBody public ListRoomType list() { return roomTypeService.listAll(); } }这一段看起来简单但至少卡住过三个人一个是resultType忘写导致映射失败一个是Mapper XML的namespace写错导致找不到方法还有一个是ResponseBody返回JSON时Jackson依赖没加页面直接白屏。启动Tomcat后访问http://localhost:8080/hotel-ssm/roomType/list能返回JSON就说明框架链路通了再往上盖酒店业务才有底气。3. 酒店管理系统的数据库设计从表结构到初始化数据3.1 核心表拆成几张让“数据关系”在答辩时说得清酒店管理系统的表结构我一般拆五张核心表t_admin管理员表、t_customer顾客表、t_room_type房间类型表、t_room房间表、t_order订单表。有人还会加消费记录表但大作业做到第五张表已经能讲清楚了。核心是t_order它把顾客、房间、房价、时间串在一起。DDL如下直接跑在一个空库里就行CREATE DATABASE hotel_ssm DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE hotel_ssm; CREATE TABLE t_admin ( id INT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(30) NOT NULL UNIQUE, password VARCHAR(64) NOT NULL, real_name VARCHAR(50), role VARCHAR(20) DEFAULT admin, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE t_customer ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, id_card VARCHAR(20) NOT NULL, phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE t_room_type ( id INT AUTO_INCREMENT PRIMARY KEY, type_name VARCHAR(30) NOT NULL, price DECIMAL(10,2) NOT NULL, bed_num TINYINT DEFAULT 2, area INT, remark VARCHAR(200) ); CREATE TABLE t_room ( id INT AUTO_INCREMENT PRIMARY KEY, room_no VARCHAR(10) NOT NULL UNIQUE, room_type_id INT NOT NULL, floor TINYINT, status TINYINT DEFAULT 0 COMMENT 0-空闲 1-已入住 2-打扫中 3-维修, CONSTRAINT fk_room_type FOREIGN KEY (room_type_id) REFERENCES t_room_type(id) ); CREATE TABLE t_order ( id INT AUTO_INCREMENT PRIMARY KEY, customer_id INT NOT NULL, room_id INT NOT NULL, check_in_date DATETIME, check_out_date DATETIME, actual_out_date DATETIME, price DECIMAL(10,2) COMMENT 入住时房价快照, deposit DECIMAL(10,2) DEFAULT 0, total_amount DECIMAL(10,2) DEFAULT 0, status TINYINT DEFAULT 0 COMMENT 0-已预订 1-已入住 2-已退房 3-已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_order_customer FOREIGN KEY (customer_id) REFERENCES t_customer(id), CONSTRAINT fk_order_room FOREIGN KEY (room_id) REFERENCES t_room(id) );我建议把数据库名和表名统一用t_前缀理由是答辩讲PPT时容易指认也避免把MySQL的保留字当表名。外键约束可以建但注意“删除房间”之前必须处理订单如果觉得麻烦也可以只保留逻辑外键在Java代码里维护关联但答辩被问到“你怎么保证引用完整性”时有物理外键更好解释。3.2 订单状态和房间状态两套状态字段不要写进同一个值订单表的status表达的是业务进度0已预订、1已入住、2已退房、3已取消。房间表的status表达的是房间可用性0空闲、1已入住、2打扫中、3维修。两张表的状态要联动但要分开维护因为“订单已退房”和“房间已打扫”未必同时发生。经常有同学只设一个订单状态退房时直接把订单改成“已退房”房间状态没有改第二天同房间又被卖出去数据库里出现两单撞房。正确做法是入住时同时执行“订单status1”和“房间status1”退房时执行“订单status2”和“房间status2”服务员做房完成后再把“房间status0”。状态机变化最好放在Service层统一控制而不是在Controller里一改再改。3.3 初始化数据房号规则、房价和账号密码空表没法演示初始化数据里要注意三件事房间编号规则、房型价格、管理员账号。房号我习惯用楼层两位房号比如1001、1002表示1层01/02房间2001表示2层前端下拉框也好看。价格用DECIMAL(10,2)而不是DOUBLE后面算钱才不会出现16.199999这种浮点数。INSERT INTO t_admin (username, password, real_name, role) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 系统管理员, admin), (manager, e10adc3949ba59abbe56e057f20f883e, 客房部经理, manager); INSERT INTO t_room_type (type_name, price, bed_num, area, remark) VALUES (单人间, 128.00, 1, 18, 1.8米床), (标准间, 188.00, 2, 24, 双床), (大床房, 228.00, 2, 28, 2米大床), (套房, 428.00, 2, 45, 客厅卧室); INSERT INTO t_room (room_no, room_type_id, floor, status) VALUES (1001, 1, 1, 0), (1002, 1, 1, 0), (1003, 2, 1, 1), (2001, 3, 2, 0), (2002, 4, 2, 0); INSERT INTO t_customer (name, id_card, phone) VALUES (张三, 110101199001011234, 13800138000);上面的password是“123456”的MD5值e10adc3949ba59abbe56e057f20f883e。如果你在代码里直接用明文比对这行就废了如果不想做MD5也可以把password改成明文但答辩容易被追问安全隐患。我一般建议至少做个MD5这是最简单也最能拿去讲的“安全设计”。3.4 权限设计不用堆五张RBAC表一个角色字段就够用毕业设计的权限管理做到“管理员登录才能访问后台、不同角色看到不同菜单”这个粒度就差不多了。t_admin表里加了一个role字段admin对应超级管理员manager对应普通管理员。登录成功后把对象放进Session然后用一个SpringMVC拦截器统一判断。拦截器里判断Session中的admin是否为空为空就跳登录页。如果还要细分权限在拦截器里再判断一下rolepublic class AdminInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Admin admin (Admin) session.getAttribute(admin); if (admin null) { response.sendRedirect(request.getContextPath() /admin/login); return false; } if (manager.equals(admin.getRole()) request.getRequestURI().contains(/admin/user)) { response.sendRedirect(request.getContextPath() /index); return false; } return true; } }别一上来就上Spring Security课设里那套登录流程和十几个跳转可能会让配置越调越乱。手写拦截器简单、可控答辩时还能讲出“这是AOP思想的体现”比你抄一份看不懂的框架代码强得多。4. 从页面到接口把登录、入住、退房三个主流程串起来4.1 登录页与Session把对象放进去别把密码放进去前端页面用JSP足够登录表单提交到/admin/loginController里先按用户名查出admin对象再比对密码。比对的逻辑要写在Web层之外的Service里这样拦截器、测试都能复用。核心代码Controller RequestMapping(/admin) public class AdminController { Autowired private AdminService adminService; RequestMapping(/login) public String login(String username, String password, HttpSession session, Model model) { Admin admin adminService.findByUsername(username); if (admin null) { model.addAttribute(error, 用户名不存在); return login; } if (!admin.getPassword().equals(Md5Util.md5(password))) { model.addAttribute(error, 密码错误); return login; } session.setAttribute(admin, admin); return redirect:/index; } }Md5Util是自己写的一个工具类里面用MessageDigest把用户输入的密码做一次MD5再和数据库里的密文比对。登录成功后Session里存的是admin对象页面直接用${sessionScope.admin.realName}显示当前登录人。千万不要把明文密码也塞进去避免JSP输出时不注意打了password字段。4.2 入住登记两步写操作请放进一个事务入住登记本来不复杂但涉及两张表先给t_order插一条状态为“已入住”的记录再把t_room的状态改成“已入住”。如果这两步不是原子操作就会出现订单生成但房态没变或者房态变了但订单没成的脏数据。我习惯把业务写在OrderService里加Transactional。Service public class OrderServiceImpl implements OrderService { Autowired private OrderMapper orderMapper; Autowired private RoomMapper roomMapper; Transactional(rollbackFor Exception.class) public Integer checkIn(Order order) { Room room roomMapper.selectById(order.getRoomId()); if (room null || room.getStatus() ! 0) { throw new RuntimeException(房间不存在或已被占用); } order.setStatus(1); order.setPrice(room.getPrice()); order.setCheckInDate(new Date()); orderMapper.insert(order); Room update new Room(); update.setId(order.getRoomId()); update.setStatus(1); roomMapper.updateStatus(update); return order.getId(); } }这段代码有几个细节容易被忽略。第一price要取房间当前价格写入订单而不是前台传多少钱就收多少钱因为退房时房型可能调价了订单里的价格才是“快照”。第二orderMapper.insert要拿到自增主键Mapper XML里得加useGeneratedKeystrue keyPropertyid否则order.getId()是null。第三事务方法必须public并且从Controller其他Service调用不能在实现类内部用this.checkIn()自己调用否则代理切不进去。4.3 退房结账延时费、押金抵扣和金额快照退房是最容易翻车的页面因为涉及天数计算、延时费和押金。我给出的算法是先算出应入住天数和实际入住天数再判断是否超出预计退房时间超出6小时加收半天房费最后把总金额写回订单。public double calculateOrderAmount(Order order) { if (order.getCheckInDate() null || order.getActualOutDate() null) { return BigDecimal.ZERO.doubleValue(); } long hours Duration.between(order.getCheckInDate(), order.getActualOutDate()).toHours(); int days (int) Math.ceil(hours / 24.0); if (days 0) { days 1; } BigDecimal roomFee order.getPrice().multiply(BigDecimal.valueOf(days)); BigDecimal overtimeFee BigDecimal.ZERO; if (order.getCheckOutDate() ! null order.getActualOutDate().after(order.getCheckOutDate())) { long overHours Duration.between(order.getCheckOutDate(), order.getActualOutDate()).toHours(); if (overHours 6) { overtimeFee order.getPrice().multiply(BigDecimal.valueOf(0.5)); } } return roomFee.add(overtimeFee).doubleValue(); }这里避开了两个坑一是天数计算不用自然日而是按小时向上取整避免“住了一晚但跨天”的误判二是金额运算用BigDecimal不能用double直接乘。如果你连退房流程都做不完也要保证“房态能释放”最少把实际退房时间、订单状态、房间状态三个字段更新正确。押金逻辑我一般简化成“退房时总金额先从押金中抵扣多退少补”代码里只多一步减法答辩前记得在演示里说清。4.4 订单查询和分页MyBatis动态SQL是烤肉店里的孜然后台管理页面通常需要按“订单状态、顾客姓名、入住时间”筛选订单这种多条件组合用MyBatis的动态SQL是最舒服的。Mapper接口接收一个Map或VOXML里用where、if拼接参数不需要的语句不会进SQL避免了“拼SQL同时要拼and”的恶心事。select idsearchOrders resultTypeOrder parameterTypemap SELECT o.*, c.name AS customerName, r.room_no AS roomNo FROM t_order o LEFT JOIN t_customer c ON o.customer_id c.id LEFT JOIN t_room r ON o.room_id r.id where if teststatus ! null AND o.status #{status} /if if testcustomerName ! null and customerName ! AND c.name LIKE CONCAT(%, #{customerName}, %) /if if testbeginDate ! null and beginDate ! AND o.check_in_date gt; #{beginDate} /if if testendDate ! null and endDate ! AND o.check_out_date lt; #{endDate} /if /where ORDER BY o.create_time DESC /select注意小于号必须用、大于号用不然XML解析会报错。首页和分页可以手写也可以引入PageHelper插件推荐PageHelper因为一行PageHelper.startPage(pageNum, pageSize)就能接住列表再包一层PageInfo就能拿到total。5. SSMMySQL酒店管理系统避坑指南五个让新手卡到凌晨的问题5.1 启动Tomcat报ClassNotFoundException依赖没打进去现象IDEA里编译没有错一启动Tomcat就抛NoClassDefFoundError或ClassNotFoundException最常见的是spring-webmvc / mybatis-spring这类类找不到。还有的报错在Tomcat启动日志里写着“Unable to instantiate Servlet [dispatcher]”。原因pom.xml明明引入了依赖IDEA的Dependencies里也能看到但部署到Tomcat时没有把依赖的jar包打进WEB-INF/lib。Maven工程必须用war包方式打包IDEA的Tomcat部署也依赖Artifacts配置如果你直接导入源码本地Maven仓库又缺这些jar情况就会雪上加霜。解决在Maven面板执行mvn package然后用压缩工具打开生成的war确认WEB-INF/lib里有几十个jar。IDEA用Tomcat启动时在File→Project Structure→Artifacts里检查“Available Elements”是否把library files加入右侧的Output Layout。如果运行环境是Tomcat 10还要换成jakarta.servlet-api依赖并把Spring版本升级到6.x或把运行环境换回Tomcat 9这两个办法选一个否则永远起不来。5.2 页面和数据库全是中文问号三层字符集一起乱现象登录页输入“管理员”提交后显示的欢迎语是“????”MySQL里t_admin表也存成了问号。更隐蔽的是直接写进页面的中文正常管理员从数据库读出来的中文就乱。原因乱码不是一个环节的问题。前端表单默认按页面编码提交Tomcat默认用ISO-8859-1解码MySQL连接没带characterEncoding参数表字段默认又是latin1这四个环节只要有一层不一致中文就走样。只改JDBC URL不改过滤器只改过滤器不变表结构都会白忙。解决先把四层统一成UTF-8。web.xml里配置CharacterEncodingFilterurl-pattern用/*它在请求进入Controller前把请求体和响应都转成UTF-8。JSP页面顶部加% page contentTypetext/html;charsetUTF-8 %并把IDE右下角编码切到UTF-8。JDBC URL追加useUnicodetruecharacterEncodingutf8。最后把已有的表转成utf8mb4ALTER TABLE t_admin CONVERT TO CHARACTER SET utf8mb4;。四步顺序不能乱改完一步就重启验证一次。5.3 MySQL 8连接报Public Key Retrieval is not allowed时区和认证插件现象本地项目刚启动没问题跑几小时重启后报“Public Key Retrieval is not allowed for user”或者数据库端是MariaDB、连接端还是MySQL驱动直接给你抛“Communications link failure”。原因MySQL 8默认用caching_sha2_password认证插件JDBC需要服务端的公钥来完成密码加密传输旧驱动或没打开allowPublicKeyRetrieval时连接拒绝。时区参数没指定时MySQL会读系统时区而Linux下的CST或“中国标准时间”这类写法JDBC不认识于是爆出unrecognized time zone。解决url改为jdbc:mysql://localhost:3306/hotel_ssm?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue同时确认驱动是8.x。如果还是报时区在MySQL命令行执行SET GLOBAL time_zone08:00;。如果是MariaDB把依赖换成org.mariadb.jdbc:mariadb-java-client并按MariaDB的驱动类名org.mariadb.jdbc.Driver修改driverClassName。5.4 事务没生效订单插进去了房态没更新引擎和方法可见性现象入住登记成功订单表里新记录出现了但房间状态永远停在“空闲”或者反过来退房时房间释放了订单金额没算出来数据对不上。原因第一表引擎是MyISAM它根本不支持事务InnoDB语法没作用。第二Transactional加在private方法上或同一个Service实例内部调用另一个方法SpringAOP代理没法切到私有方法和内部调用事务等于摆设。第三Service里catch异常后没重新抛Spring判定业务正常提交了自然不会回滚。解决先执行SHOW TABLE STATUS WHERE Namet_order;Engine列如果不是InnoDB就执行ALTER TABLE t_order ENGINEInnoDB;同时把其他业务表全部转成InnoDB。再检查Service方法事务方法必须public而且必须从Controller或其他Service调用不能在实现类内部用this.checkIn()这种写法。最后检查catch代码块至少throw new RuntimeException(退房失败, e);把异常抛出去。5.5 部署到服务器后样式全丢、上传图片神秘消失路径写死是万恶之源现象本地用IDEA内置Tomcat调试页面CSS/JS正常打成war丢到Linux后样式加载404上传的图片只在当前会话能看重启就丢。原因JSP里写了相对路径比如../static/css/main.css本地contextPath是/依赖正常改到服务器后contextPath变成/hotel-ssm相对路径就错位。图片上传保存到了项目webapps下的一个临时目录Tomcat每次clean或重启都会清理有时还会被系统临时文件清理程序删掉。解决JSP里统一用%request.getContextPath()%或${pageContext.request.contextPath}拼绝对路径。上传文件改为保存到配置的磁盘绝对路径比如upload.path/data/hotel/uploads/代码读取该配置同时在SpringMVC配置里添加mvc:resources mapping/uploads/** locationfile:/data/hotel/uploads//把磁盘路径映射成URL可访问。这样即使重启图片也还在磁盘上主机的路径也不会因为部署位置而漂移。6. 验证与进阶给自己的酒店管理系统写一份验收清单系统写完之后别急着打包交差先按“核心流程”走一遍验收清单我把答辩时常用的流程列成一张表步骤操作预期结果1使用admin身份登录跳转后台首页顶部显示管理员姓名2查看房态列表1001、1002空闲1003显示已入住3新增一条房间类型房型列表出现新类型价格用DECIMAL显示4为张三办理入住选1001订单状态变成已入住房态列表1001变成已入住5模拟退房设置实际退房时间晚2小时订单状态变已退房金额1天房费房间状态变打扫中6把房间1001标记为打扫完成房态列表恢复空闲7用管理员manager登录能进首页但不能访问用户管理页8用错误密码登录页面出现错误提示不跳转演示时按这张表走每个操作都能对应一个业务模块答辩老师一般不会再难为你。如果时间富余还可以在“订单管理”页加一个简单的分组统计按订单状态展示柱状图或饼图图表数据用MyBatis的group by查出来返回JSON前端用ECharts绘制这是最廉价的“系统亮点”。再往上进阶方向很明确把SpringMVCJSP换成Spring BootVue后端接口保留不变数据库不用改把MyBatis升级成MyBatis Plus消除大量BaseMapper样板代码房态查询加一个Redis缓存避免每次刷新都打MySQL退房金额计算抽成策略模式方便以后增加钟点房、长租房计费规则。这些升级每做一步都依赖你现在这套SSM基础。我经手过不少毕业设计项目最大的教训就是跑不起来的代码等于零。提前一天把Maven环境、MySQL脚本、Tomcat版本固化好比临答辩前再去调任何一个变量都值。后面的路你可以按这份清单把项目完整走一遍再把每个模块的日志打出来做到每点一个按钮都知道它的执行链路答辩基本稳了。希望帮到你。本文还有配套的精品资源点击获取
返回列表