资讯动态

SSM餐厅点餐系统实战:从搭建到生产部署

发布时间:2026/9/16 13:19:43 来源:尧图企业网站定制
简介这是一套基于SSM框架开发的B/S架构餐厅点餐管理系统面向计算机专业本科生及Java初学者适用于毕业设计、课程设计与Web全栈开发入门实践。系统实现用户端点餐与管理员端菜品管理两大核心功能涵盖注册登录、菜单浏览、订单提交、后台商品增删改查等完整业务流程技术栈覆盖前端HTML/CSS/JS/JQuery与后端JSPSpringSpringMVCMyBatis运行依赖JDK1.8、Tomcat8.0及IDEA/Eclipse开发环境。资源包共2000个文件以981个HTML页面、255个JS交互脚本、222个Java业务类、141个JSP视图及123个XML配置文件为主体辅以CSS样式、图片资源与SQL数据库脚本整体46.66MB结构清晰、模块分明便于逐层理解MVC分层逻辑与前后端协作机制。目前已有330人学习下载配套代码完整、注释较充分可直接部署运行是掌握SSM整合开发与餐饮类业务系统建模的优质参考案例。1. 为什么还在用 SSM 做餐厅点餐系统不是过时而是够用、可控、易交付很多新人看到“基于SSM的餐厅点餐管理系统”第一反应是都2024年了怎么还用 Spring Spring MVC MyBatis其实这恰恰说明项目定位清晰——它不是要跑在百万并发的外卖平台而是面向中小型实体餐厅的本地化部署系统后厨打印机实时出单、前台平板快速选桌、经理端导出当日流水、服务员扫码确认上菜。这类场景不需要 Spring Cloud 微服务拆分也不必强求 Vue3 TypeScript 前后端分离SSM 的三层结构Controller 层接收请求、Service 层封装业务、DAO 层操作数据库天然契合“点餐→下单→备餐→结账→统计”的线性流程开发周期可控、运维成本低、团队上手快。尤其对 Java 初级工程师或校企合作项目而言SSM 是少有的既能练透 MVC 分层思想、又能完整走通数据库建模→接口开发→页面联调→部署上线全链路的成熟技术栈。本文不讲“SSM 已死”只说清楚如何用 SSM 搭出一个真正能进店用、不崩、好改、查得清的餐厅点餐系统。2. 从零搭建 SSM 餐厅点餐系统环境、依赖与核心配置三件套2.1 开发环境与版本选型稳定压倒一切SSM 并非越新越好。生产级餐厅系统更看重长期兼容性与社区支持广度。我们采用经大量中小项目验证的组合JDK 8u291避免 JDK 17 在 Tomcat 9 下的反射兼容问题Tomcat 9.0.83Servlet 4.0兼容 Spring 5.3.xSpring 5.3.31Spring 6 要求 JDK 17舍弃MyBatis 3.4.6MyBatis 3.5 对 Oracle 11g 兼容性变弱而不少老餐厅仍用 OracleMySQL 5.7.36InnoDB 引擎支持事务回滚保障“下单失败不扣库存”提示不要直接用spring-boot-starter-web替代传统 SSM。本系统需显式控制 DispatcherServlet 加载顺序、MyBatis SqlSessionFactory 初始化时机以及静态资源如菜品图片的 Tomcat 直接映射路径——这些在 Spring Boot 自动装配下反而难调试。2.2 Maven 依赖配置精简且可追溯pom.xml中必须显式声明以下核心依赖禁用传递依赖自动拉取避免mybatis-spring版本与spring-core冲突!-- Spring 核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.3.31/version /dependency dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.3.31/version /dependency !-- MyBatis 整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.4.6/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version1.3.2/version /dependency !-- 数据库驱动以 MySQL 为例 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.47/version scoperuntime/scope /dependency !-- 连接池HikariCP 启动慢Druid 更适合小系统监控 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.16/version /dependency2.2.1 为什么选 Druid 而非 HikariCPDruid 内置/druid/login.html监控页餐厅管理员可随时查看 SQL 执行耗时、慢查询TOP5、连接池活跃数——当某天“结账卡顿”不用翻日志直接看 Druid 页面就能定位是update order_status还是insert into order_detail慢支持stat,wall,config三类 Filter其中wallFilter 可拦截delete from table全表删操作防止误点“清空今日订单”按钮导致数据丢失配置项removeAbandonedOnBorrowtrue能自动回收服务员长时间未提交的“暂存订单”连接避免连接池被占满。2.3 核心 XML 配置三层解耦的关键锚点SSM 的灵魂不在注解而在applicationContext.xml、spring-mvc.xml、mybatis-config.xml三份 XML 的职责划分。这是新手最容易混淆、也是线上故障高发区。2.3.1applicationContext.xml只管 Bean 容器与事务此文件不加载 Controller只声明 Service、DAO、DataSource、TransactionManager!-- 数据源Druid -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource init-methodinit destroy-methodclose property nameurl valuejdbc:mysql://localhost:3306/restaurant?useSSLfalseamp;serverTimezoneAsia/Shanghai/ property nameusername valueroot/ property namepassword value123456/ property nameinitialSize value5/ property namemaxActive value20/ property nameminIdle value5/ property nametimeBetweenEvictionRunsMillis value60000/ /bean !-- MyBatis SqlSessionFactory -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property nameconfigLocation valueclasspath:mybatis-config.xml/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean !-- 事务管理器 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean !-- 启用 Transactional 注解 -- tx:annotation-driven transaction-managertransactionManager/注意tx:annotation-driven必须与transactionManagerBean ID 严格一致否则Transactional失效——这是餐厅系统最隐蔽的坑下单时库存扣减成功但订单未生成因事务未生效。2.3.2spring-mvc.xml只管 Web 层绝不碰数据库此文件不声明 DataSource 或 SqlSessionFactory专注请求路由、视图解析、JSON 转换!-- 扫描 Controller 包 -- context:component-scan base-packagecom.restaurant.controller use-default-filtersfalse context:include-filter typeannotation expressionorg.springframework.stereotype.Controller/ /context:component-scan !-- 视图解析器JSP 页面放在 /WEB-INF/jsp/ 下 -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/jsp// property namesuffix value.jsp/ /bean !-- 支持 RequestBody 接收 JSON -- mvc:annotation-driven mvc:message-converters register-defaultstrue bean classorg.springframework.http.converter.json.MappingJackson2HttpMessageConverter property nameobjectMapper bean classcom.fasterxml.jackson.databind.ObjectMapper property namedateFormat bean classjava.text.SimpleDateFormat constructor-arg valueyyyy-MM-dd HH:mm:ss/ /bean /property /bean /property /bean /mvc:message-converters /mvc:annotation-driven !-- 静态资源放行菜品图片、CSS、JS -- mvc:resources mapping/static/** location/static//2.3.3mybatis-config.xmlSQL 与 POJO 的契约中心此文件不写任何业务 SQL只做全局设置与类型别名?xml version1.0 encodingUTF-8? !DOCTYPE configuration PUBLIC -//mybatis.org//DTD Config 3.0//EN http://mybatis.org/dtd/mybatis-3-config.dtd configuration settings !-- 开启二级缓存适合菜品信息等不常变数据 -- setting namecacheEnabled valuetrue/ !-- 驼峰命名自动转换数据库 order_id → Java orderId -- setting namemapUnderscoreToCamelCase valuetrue/ !-- 打印 SQL 日志上线前必须设为 false -- setting namelogImpl valueSTDOUT_LOGGING/ /settings typeAliases package namecom.restaurant.entity/ /typeAliases /configuration3. 餐厅核心业务落地从数据库建模到 Controller 接口的完整闭环3.1 数据库设计紧扣“人-桌-菜-单”四要素餐厅点餐系统本质是状态机顾客就座 → 选菜 → 提交 → 后厨打印 → 上菜确认 → 结账完成。数据库必须支撑该状态流转拒绝过度设计如不建“用户角色表”服务员/经理权限用字段标识即可表名关键字段说明t_tabletable_id,table_name,status(0空闲/1占用/2清洁中)桌位状态实时可见避免顾客被带错桌t_dishdish_id,dish_name,price,category,is_available(0下架/1上架)is_available控制菜品是否出现在点餐页比删数据更安全t_orderorder_id,table_id,status(0待支付/1已支付/2已完成),create_time,pay_time主订单含桌号、总金额、状态不存明细t_order_detaildetail_id,order_id,dish_id,quantity,remark(如“微辣”)明细表支持同一订单多道同菜例2份宫保鸡丁提示t_order.status必须用TINYINT而非VARCHAR。当后厨大屏轮询“待制作订单”时WHERE status 0比WHERE status pending查询快 3 倍以上且避免大小写拼写错误。3.2 Service 层实现用事务保证“下单即锁库存”点餐最核心逻辑是“提交订单时菜品库存必须实时扣减且整个过程不可中断”。若用两步操作先查库存→再扣减高并发下必然超卖。正确做法是SQL 级乐观锁 Service 层事务兜底Service public class OrderService { Transactional(rollbackFor Exception.class) public boolean createOrder(Order order, ListOrderDetail details) { // 1. 插入主订单获取自增 order_id orderMapper.insert(order); // 2. 批量插入明细 for (OrderDetail detail : details) { detail.setOrderId(order.getOrderId()); orderDetailMapper.insert(detail); } // 3. 扣减库存关键用 WHERE 子句原子判断 for (OrderDetail detail : details) { int affected dishMapper.reduceStock( detail.getDishId(), detail.getQuantity() ); if (affected 0) { throw new RuntimeException(菜品【 detail.getDishName() 】库存不足); } } return true; } }3.2.1reduceStock的 Mapper XML 实现!-- DishMapper.xml -- update idreduceStock parameterTypemap UPDATE t_dish SET stock stock - #{quantity} WHERE dish_id #{dishId} AND stock #{quantity} /update注意AND stock #{quantity}是核心。MyBatis 返回affectedRows为 0说明库存不足事务自动回滚主订单和明细全部撤销——这是 SSM 能稳住餐厅系统的底层保障。3.3 Controller 层RESTful 风格接口与前端联调要点前端JSP jQuery通过 AJAX 调用Controller 必须返回明确状态码与结构化 JSONController RequestMapping(/order) public class OrderController { Autowired private OrderService orderService; /** * 提交订单POST /order/create * 请求体{tableId:1,details:[{dishId:101,quantity:2,remark:微辣}]} */ RequestMapping(value /create, method RequestMethod.POST) ResponseBody public Result createOrder(RequestBody OrderRequest request) { try { Order order new Order(); order.setTableId(request.getTableId()); order.setStatus(0); // 待支付 order.setCreateTime(new Date()); ListOrderDetail details request.getDetails().stream() .map(d - { OrderDetail od new OrderDetail(); od.setDishId(d.getDishId()); od.setQuantity(d.getQuantity()); od.setRemark(d.getRemark()); return od; }).collect(Collectors.toList()); boolean success orderService.createOrder(order, details); return Result.success(下单成功, order.getOrderId()); } catch (RuntimeException e) { return Result.error(e.getMessage()); } } }3.3.1 前端联调必须验证的三个响应头响应头值作用Content-Typeapplication/json;charsetUTF-8防止中文乱码如菜品名“麻婆豆腐”Access-Control-Allow-Origin*或具体域名若前端用 Nginx 代理此处必须配否则跨域报错Cache-Controlno-cache禁用浏览器缓存确保每次请求都是最新订单状态4. 生产环境必调的 3 个参数与 2 类日志排查法4.1 Tomcat 连接池参数防住午市高峰的并发冲击餐厅系统峰值在 11:30–13:00 和 17:30–19:00。默认 Tomcat 连接池maxThreads200在 50 服务员同时刷单时会排队。需在conf/server.xml中调整Connector port8080 protocolHTTP/1.1 maxThreads500 !-- 提升至 500并发承载力翻倍 -- minSpareThreads50 !-- 预热 50 线程避免冷启动延迟 -- acceptCount100 !-- 排队队列长度超过则拒绝返回 503 -- connectionTimeout20000 !-- 连接超时 20 秒防住慢 SQL 拖垮整个池 -- redirectPort8443 /提示acceptCount100是安全阀。当午市瞬时 600 请求涌入前 500 进线程池处理后 100 进队列第 601 个请求直接返回 503前端可提示“系统繁忙请稍后再试”而非让用户无限等待白屏。4.2 Druid 监控页密码保护防止订单数据泄露/druid/login.html默认无密码一旦部署到公网任何人都能查到所有 SQL含顾客手机号、消费金额。必须在web.xml中加 Basic Authservlet servlet-nameDruidStatView/servlet-name servlet-classcom.alibaba.druid.support.http.StatViewServlet/servlet-class init-param param-nameloginUsername/param-name param-valueadmin/param-value /init-param init-param param-nameloginPassword/param-name param-valueRestaurant2024/param-value /init-param /servlet servlet-mapping servlet-nameDruidStatView/servlet-name url-pattern/druid/*/url-pattern /servlet-mapping4.3 两类日志定位法快速揪出“下单失败”真凶当餐厅反馈“点完菜没反应”不要先查代码按顺序看日志4.3.1 Tomcat access 日志确认请求是否抵达路径logs/access_log.yyyy-mm-dd.log查找关键词POST /order/create正常应有类似行192.168.1.100 - - [15/Jul/2024:12:05:23 0800] POST /order/create HTTP/1.1 200 45若无此行 → 请求未到 TomcatNginx 配置错误或网络不通若状态码为404→ Controller 路径写错检查RequestMapping若状态码为500→ 进入下一步查catalina.out4.3.2 catalina.out 日志捕获 Spring 异常堆栈搜索createOrder或reduceStockCaused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction ... at com.restaurant.mapper.DishMapper.reduceStock(DishMapper.java:45)此错误表明t_dish表被长事务锁住如某服务员开单后未提交一直停留在“确认上菜”页。解决方案在DishMapper.xml的reduceStockSQL 后加FOR UPDATEMySQL 行锁前端增加“下单超时自动取消”倒计时120秒未支付则释放库存。5. 部署与上线 checklist让系统真正进店可用5.1 静态资源独立部署加速菜品图片加载餐厅平板访问/static/dish/101.jpg时不应经过 Tomcat Servlet 解析。在conf/server.xml的Host内添加Context path/static docBase/opt/restaurant/static reloadablefalse/将所有菜品图片、CSS、JS 放到服务器/opt/restaurant/static/目录下。这样/static/dish/101.jpg请求直接由 Tomcat 文件系统返回不走 Spring MVC 流程首屏加载快 200ms。5.2 数据库初始化脚本一行命令恢复营业准备init.sql包含建库、建表、初始数据如默认桌号、热门菜品CREATE DATABASE IF NOT EXISTS restaurant DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci; USE restaurant; -- 创建 t_table 表略 INSERT INTO t_table (table_name, status) VALUES (A01, 0), (A02, 0), (B01, 0); -- 创建 t_dish 表略 INSERT INTO t_dish (dish_name, price, category, is_available) VALUES (宫保鸡丁, 38.00, 热菜, 1), (米饭, 2.00, 主食, 1);上线时只需执行mysql -u root -p /opt/restaurant/init.sql提示utf8mb4是必须项。若用utf8顾客微信昵称“‍”存入数据库会变问号结账小票打不出头像。5.3 启动脚本标准化避免“在我机器上是好的”编写start.sh固化 JVM 参数与日志路径#!/bin/bash JAVA_HOME/usr/java/jdk1.8.0_291 export JAVA_HOME $JAVA_HOME/bin/java \ -Xms512m -Xmx1024m \ -XX:UseG1GC \ -Dfile.encodingUTF-8 \ -Dlog4j.configurationFilefile:/opt/restaurant/conf/log4j2.xml \ -jar /opt/restaurant/restaurant.war \ /opt/restaurant/logs/catalina.out 21 echo $! /opt/restaurant/pid.txt-Xms512m -Xmx1024m避免频繁 GC 影响点餐响应-Dlog4j.configurationFile强制指定日志配置防止 classpath 冲突 ... 21 后台运行并重定向日志方便tail -f logs/catalina.out实时追踪。最后一步把start.sh加入 Linux 开机自启/etc/rc.local确保断电重启后系统自动恢复营业。这才是餐厅老板要的“能用”。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价