JavaWeb甜品管理系统实战:从环境搭建到库存扣减的完整案例
简介这份资源是大三上学期JavaWeb期末项目的完整源码包面向正在学习JavaWeb课程、需要完成课设或期末大作业的本科生。项目以甜品管理系统为主题涵盖用户管理、商品管理、订单处理、购物车等典型电商业务模块适合用来理解Servlet、JSP与DAO分层开发的整体流程。压缩包共197个文件约6.05MB其中46个java源文件与65个class编译文件构成核心业务逻辑24个jsp页面负责前端展示另有36个png及少量gif、jpg图片资源、6个jar依赖包以及sql建表脚本、css样式、js脚本和xml配置等辅助文件结构完整、层次分明。已有98人学习下载。读者可直接获得一套经过测试、可运行的课设参考方案对照源码梳理Servlet跳转、数据库连接与前后端交互的实现思路也可在此基础上修改主题或扩展功能用于课程答辩与二次开发。1. 大三上学期 JavaWeb 期末项目甜品管理系统到底在考什么很多同学拿到「甜品管理系统」这个题目第一反应是去搜一套完整源码改个名字交上去。但真正答辩时被问「你的库存扣减为什么放在 Service 层而不是 Controller」就答不上来。这个项目的本质是用一个业务闭环甜品上架 → 用户下单 → 库存扣减 → 订单状态流转把 JavaWeb 的核心链路串起来Servlet 或 Spring MVC 接请求、Service 写业务、DAO 落库、JSP 或前端页面渲染。它适合正在学 JavaWeb、需要交期末作业、又想顺便把 SSM 或 SpringBoot 跑通的人。下面我按「能跑起来 → 能讲清楚 → 能改得动」的顺序拆一遍重点放在配置、参数和踩坑上而不是贴一堆没注释的代码。2. 环境搭建与项目骨架从 IDEA 到能跑通第一个接口2.1 JDK、Tomcat、Maven 的版本对齐JavaWeb 项目翻车八成死在版本上。我一般会先确认三件事JDK 用 8 还是 11、Tomcat 用 8.5 还是 9、Maven 的编译插件版本。这三者不匹配会出现「编译通过但启动 404」或者「class 文件版本不对」的玄学问题。组件推荐版本说明JDK8 或 11学校机房多为 8SpringBoot 2.7 以下建议 8Tomcat8.5 / 9.0Tomcat 10 把 javax 换成 jakarta老项目直接崩Maven3.6低于 3.5 拉依赖容易超时MySQL5.7 / 8.08.0 要改驱动类和时区参数注意如果你的项目用的是javax.servlet包就绝对不要用 Tomcat 10否则启动报ClassNotFoundException。这是最常见的「IDEA 运行 JavaWeb 项目配置」翻车点。2.2 用 Maven 建 Web 骨架的最小 pom不用 IDE 模板手写一个最小pom.xml反而更清楚依赖从哪来。下面这段是我常用的骨架打包方式必须是war否则 Tomcat 不认。project xmlnshttp://maven.apache.org/POM/4.0.0 modelVersion4.0.0/modelVersion groupIdcom.dessert/groupId artifactIddessert-mgr/artifactId version1.0/version packagingwar/packaging !-- Web 项目必须 war -- properties maven.compiler.source8/maven.compiler.source maven.compiler.target8/maven.compiler.target project.build.sourceEncodingUTF-8/project.build.sourceEncoding /properties dependencies !-- Servlet APIscope 必须是 providedTomcat 已自带 -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- MySQL 驱动8.0 用 com.mysql.cj.jdbc.Driver -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.28/version /dependency !-- JSP 支持 -- dependency groupIdjavax.servlet.jsp/groupId artifactIdjavax.servlet.jsp-api/artifactId version2.3.3/version scopeprovided/scope /dependency /dependencies /project逻辑说明provided表示这个依赖由运行环境Tomcat提供不打进 war 包否则会和 Tomcat 自带的 Servlet 冲突报NoSuchMethodError。参数上maven.compiler.source/target要和你的 JDK 一致用 JDK 11 编译却写 8可能出现Unsupported class file major version。2.3 目录结构与 web.xml 的取舍标准 Maven Web 目录是src/main/webapp里面放WEB-INF/web.xml和 JSP。Servlet 3.0 以后可以用WebServlet注解代替 web.xml 配置但期末项目里老师往往要求看到 web.xml所以我一般两个都留注解写映射web.xml 写欢迎页和编码过滤器。!-- web.xml 里配置字符编码过滤器解决中文乱码 -- 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如果没用 Spring就自己写一个Filter实现类在doFilter里request.setCharacterEncoding(UTF-8)。这一步不做甜品名称里的中文存进数据库就是问号答辩演示时非常尴尬。3. 数据库设计与甜品管理系统的核心表3.1 四张表撑起整个业务闭环甜品管理系统的表不用多但关系要清楚。我一般设计四张用户表、甜品表、订单表、订单明细表。订单和甜品是多对多靠订单明细拆开。CREATE TABLE dessert ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, category VARCHAR(20), status TINYINT DEFAULT 1 -- 1 上架 0 下架 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, total DECIMAL(10,2), status TINYINT DEFAULT 0, -- 0 待支付 1 已支付 2 已取消 create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE order_item ( id INT PRIMARY KEY AUTO_INCREMENT, order_id INT NOT NULL, dessert_id INT NOT NULL, num INT NOT NULL, price DECIMAL(10,2) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;参数说明utf8mb4而不是utf8因为甜品名可能有 emoji 或生僻字DECIMAL(10,2)存金额绝不能用FLOAT否则 0.10.2 会出现精度误差对账时对不上。stock字段是后面并发扣减的关键先记住它。3.2 JDBC 工具类的三个必调参数不用框架的话写一个DBUtil拿连接。URL 里三个参数最容易漏useSSL、serverTimezone、characterEncoding。public class DBUtil { private static final String URL jdbc:mysql://localhost:3306/dessert?useSSLfalse serverTimezoneAsia/Shanghai // 8.0 驱动必须指定时区 characterEncodingutf8 // 防止中文乱码 allowPublicKeyRetrievaltrue; // 8.0 首次连接需要 private static final String USER root; private static final String PWD 123456; public static Connection getConn() throws SQLException { return DriverManager.getConnection(URL, USER, PWD); } }逻辑说明serverTimezone不写MySQL 8.0 会报The server time zone value is unrecognizedallowPublicKeyRetrievaltrue是 8.0 默认加密方式导致的不加会报Public Key Retrieval is not allowed。这两个报错在「JavaWeb 项目完整案例 MySQL」里出现频率极高。3.3 DAO 层用 PreparedStatement 防注入甜品搜索、登录这些带参数的查询一律用PreparedStatement不要用字符串拼接。public Dessert findByName(String name) { String sql SELECT * FROM dessert WHERE name LIKE ?; try (Connection conn DBUtil.getConn(); PreparedStatement ps conn.prepareStatement(sql)) { ps.setString(1, % name %); // 参数化防 SQL 注入 ResultSet rs ps.executeQuery(); if (rs.next()) { Dessert d new Dessert(); d.setId(rs.getInt(id)); d.setName(rs.getString(name)); d.setPrice(rs.getBigDecimal(price)); return d; } } catch (SQLException e) { e.printStackTrace(); } return null; }参数说明setString的索引从 1 开始不是 0这是新手最常见的下标越界来源。try-with-resources保证连接自动关闭否则连接池耗尽后整个系统卡死。4. 下单与库存扣减业务层最容易出 bug 的地方4.1 库存扣减必须放在事务里下单流程是插入订单 → 插入订单明细 → 扣减库存。这三步要么全成功要么全回滚。如果扣库存失败但订单已插入就会出现「有订单没货」的脏数据。public boolean placeOrder(int userId, int dessertId, int num) { Connection conn null; try { conn DBUtil.getConn(); conn.setAutoCommit(false); // 开启事务 // 1. 扣库存带 stock num 条件防止超卖 String sql1 UPDATE dessert SET stock stock - ? WHERE id ? AND stock ?; PreparedStatement ps1 conn.prepareStatement(sql1); ps1.setInt(1, num); ps1.setInt(2, dessertId); ps1.setInt(3, num); if (ps1.executeUpdate() 0) { conn.rollback(); // 库存不足回滚 return false; } // 2. 插入订单 String sql2 INSERT INTO orders(user_id, total, status) VALUES(?,?,0); PreparedStatement ps2 conn.prepareStatement(sql2, Statement.RETURN_GENERATED_KEYS); ps2.setInt(1, userId); ps2.setBigDecimal(2, BigDecimal.ZERO); ps2.executeUpdate(); conn.commit(); // 全部成功才提交 return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) {} return false; } finally { try { if (conn ! null) conn.setAutoCommit(true); } catch (SQLException e) {} } }逻辑说明WHERE id ? AND stock ?这一句是防超卖的核心把判断和扣减合成一条原子 SQL避免「先查再扣」之间的并发窗口。参数上setAutoCommit(false)开启事务后任何一步失败都要rollback成功才commit。RETURN_GENERATED_KEYS用来拿自增订单 ID方便插入明细。4.2 订单状态流转的边界订单状态从 0待支付到 1已支付或 2已取消只能单向流转。取消订单时要把库存加回去这一步很多人漏掉。public boolean cancelOrder(int orderId) { Connection conn null; try { conn DBUtil.getConn(); conn.setAutoCommit(false); // 只有待支付订单能取消 String sql UPDATE orders SET status 2 WHERE id ? AND status 0; PreparedStatement ps conn.prepareStatement(sql); ps.setInt(1, orderId); if (ps.executeUpdate() 0) { conn.rollback(); return false; // 已支付或已取消不能重复操作 } // 回补库存 String sql2 UPDATE dessert d JOIN order_item oi ON d.id oi.dessert_id SET d.stock d.stock oi.num WHERE oi.order_id ?; conn.prepareStatement(sql2).executeUpdate(); conn.commit(); return true; } catch (SQLException e) { try { if (conn ! null) conn.rollback(); } catch (SQLException ex) {} return false; } finally { try { if (conn ! null) conn.setAutoCommit(true); } catch (SQLException e) {} } }参数说明status 0这个条件保证幂等重复点击取消不会把库存加两次。JOIN 更新是 MySQL 特有的写法Oracle 不支持如果老师要求跨库就拆成两条 SQL。4.3 分页查询的 limit 参数甜品列表要分页LIMIT offset, size里 offset 是「跳过多少条」不是页码。public ListDessert page(int pageNo, int pageSize) { int offset (pageNo - 1) * pageSize; // 页码从 1 开始 String sql SELECT * FROM dessert WHERE status 1 ORDER BY id DESC LIMIT ?, ?; // ... setInt(1, offset); setInt(2, pageSize); }注意pageNo从 1 开始如果前端传 0offset 变成负数SQL 直接报错。我一般会在 Service 层做一次Math.max(1, pageNo)兜底。5. 避坑与排查答辩前一定要过的五道关5.1 启动报 404但控制台没报错现象Tomcat 启动成功访问http://localhost:8080/dessert/list返回 404。 原因多半是war包名和访问路径不一致或者WebServlet(/list)少了项目名。IDEA 里 Application context 默认是/但 war 包名可能是dessert-mgr。 解决在 IDEA 的 Run Configuration → Deployment 里把 Application context 设成/或者访问时带上 war 包名。另外确认web.xml的metadata-complete不是true否则注解不生效。5.2 中文乱码数据库里全是问号现象页面提交「芒果班戟」数据库存成???。 原因三个环节任一没设 UTF-8——请求编码、数据库连接编码、表字符集。 解决Filter 里setCharacterEncoding(UTF-8)JDBC URL 加characterEncodingutf8建表用utf8mb4。三处都改完重启缺一不可。5.3 库存扣成负数现象并发测试时库存变成 -1。 原因用了「先 SELECT 查库存再 UPDATE 扣减」的写法两个请求同时查到库存 1都去扣。 解决改成UPDATE ... WHERE stock num的原子写法判断executeUpdate()返回值是否为 0。这是「JavaWeb 项目完整案例」里最值得写进答辩的细节。5.4 连接池耗尽系统越用越卡现象跑一段时间后所有请求卡住报Too many connections。 原因Connection 没关闭或者异常路径下漏了close。 解决统一用try-with-resources或者在finally里关闭。如果用了 Druid 或 C3P0检查maxActive配置默认 8 太小演示时并发一高就满。5.5 时间字段差 8 小时现象订单创建时间比实际早 8 小时。 原因JDBC URL 没写serverTimezone或者写了UTC。 解决改成serverTimezoneAsia/Shanghai。如果已经存了错数据用UPDATE orders SET create_time DATE_ADD(create_time, INTERVAL 8 HOUR)修回来。6. 让答辩加分的两个进阶技巧6.1 用拦截器做登录校验而不是每个 Servlet 都写一遍期末项目里登录校验如果散落在每个 Servlet 里代码又臭又长。我一般写一个LoginFilter拦/admin/*路径没登录就跳登录页。WebFilter(/admin/*) public class LoginFilter implements Filter { public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request (HttpServletRequest) req; HttpServletResponse response (HttpServletResponse) resp; Object user request.getSession().getAttribute(user); if (user null) { response.sendRedirect(request.getContextPath() /login.jsp); return; // 不放行 } chain.doFilter(req, resp); // 已登录放行 } }逻辑说明getSession().getAttribute(user)是登录成功后存进去的。参数上request.getContextPath()拿到项目名避免硬编码路径导致部署后跳转 404。这个技巧能让你的代码结构明显比同学干净答辩时老师一问「你怎么做权限控制」就有话讲。6.2 用 Postman 或浏览器控制台验证接口而不是只点页面演示前我习惯用 Postman 直接打接口确认返回 JSON 或跳转正确。比如下单接口构造userId1dessertId3num2看返回和数据库是否一致。这样能提前发现「页面能点但接口报错」的问题。另外浏览器 F12 的 Network 面板看请求参数和响应码比猜快得多。6.3 一个我踩过的坑别在 JSP 里写业务逻辑大三时我图省事在 JSP 里直接写SELECT查库存结果页面一多改一个字段要翻十几个文件。后来全部挪到 Servlet ServiceJSP 只负责展示。血泪经验JSP 里只放${}和 JSTL 标签任何Class.forName或 SQL 都不该出现。这样后期加一个「甜品分类筛选」功能只改 Service 和 DAO页面几乎不动。最后说个习惯每次改完代码先mvn clean package打一次 war确认没有编译错误再部署。我见过太多人 IDEA 里跑得好好的一打包就缺依赖。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

OpenShell:让Windows 11找回经典开始菜单的效率工具

OpenShell:让Windows 11找回经典开始菜单的效率工具

1. OpenShell 这个名字到底在说什么第一次听到 OpenShell,很多人心里会冒出一串问号:这是个终端增强工具?是某个云开发平台?还是哪家团队新开的代码仓库?其实在 Windows 老用户圈子里,OpenShell 指的是一个…

2026/10/11 2:20:09 阅读更多 →
vLLM异步调度实战:CPU/GPU重叠与KV Cache管理优化

vLLM异步调度实战:CPU/GPU重叠与KV Cache管理优化

1. 从一次显存打满说起:异步调度到底在解决什么如果你部署过 vLLM,大概率遇到过这样的场景:模型权重加载完毕,服务正常启动,前几个请求响应飞快,但并发一上来,GPU 利用率曲线就开始剧烈抖动——…

2026/10/20 9:51:36 阅读更多 →
什么是AppPorts?这款免费开源macOS应用迁移工具的完整概览

什么是AppPorts?这款免费开源macOS应用迁移工具的完整概览

什么是AppPorts?这款免费开源macOS应用迁移工具的完整概览 【免费下载链接】AppPorts 📦 A macOS utility to seamlessly migrate applications to external storage and reclaim local disk space.【一款 macOS 工具,无缝迁移应用到外部存储…

2026/10/20 15:46:15 阅读更多 →

最新新闻

房地产数据分析实战:从数据清洗到业务看板的完整复盘

房地产数据分析实战:从数据清洗到业务看板的完整复盘

接到这个项目的时候,团队里其实没人觉得它有多特别——REA,房地产数据分析专项,听起来就是把楼盘信息、成交记录、客户登记表整合成一套报表系统。但真做进去才发现,这活儿远比“搭个看板”复杂得多。它卡在业务判断和数据技术中间…

2026/10/21 9:00:09 阅读更多 →
微电网逆变器控制实战:从PQ控制到下垂控制及离并网切换全解析

微电网逆变器控制实战:从PQ控制到下垂控制及离并网切换全解析

上次在实验室折腾储能变流器,要给现成的台架加上孤岛运行能力。手头没有合适的微电网控制器,市面上的产品基本是黑盒,参数改一个都要折腾半天,我索性决定自己动手把控制和算法全部搭起来。整个过程涉及微电网中最典型的两类控制策…

2026/10/21 9:00:09 阅读更多 →
时钟周期、机器周期、指令周期:从晶振到延时的完整换算链

时钟周期、机器周期、指令周期:从晶振到延时的完整换算链

简介:这份PDF资料面向单片机初学者与嵌入式开发工程师,系统梳理时钟周期、机器周期、指令周期与总线周期四个核心时间概念,帮助读者厘清晶振频率、分频关系与指令执行耗时之间的换算逻辑,解决程序延时计算、功耗评估与性能优化中常…

2026/10/21 9:00:09 阅读更多 →
WinCC V17 VBS脚本编程实战:从报警确认到数据导出

WinCC V17 VBS脚本编程实战:从报警确认到数据导出

简介:这份《TIA Portal WinCC Engineering V17 – VBS脚本编程参考手册》面向使用西门子博途WinCC V17进行HMI开发的工程师与技术人员,帮助其在自动化项目中通过VBS脚本实现自定义交互逻辑与系统控制。手册系统梳理了VBS脚本的编程方法,涵盖A…

2026/10/21 9:00:09 阅读更多 →
单片机时钟周期、机器周期与指令周期详解:从计算到避坑

单片机时钟周期、机器周期与指令周期详解:从计算到避坑

简介:这份PDF资料围绕单片机三大核心时间概念展开,面向嵌入式初学者与需要夯实基础的开发者,帮助厘清时钟周期、机器周期与指令周期之间的层级关系及换算方法。资源包共1个PDF文件,大小约53KB,内容以文字讲解与实例计算…

2026/10/21 9:00:09 阅读更多 →
区块链安全基石:椭圆曲线密码学(ECC)原理与工程实践

区块链安全基石:椭圆曲线密码学(ECC)原理与工程实践

聊到区块链,椭圆曲线密码学(ECC)是个绕不开的名字。你手里的每一个钱包地址、每一笔链上交易的数字签名,背后几乎都能看到它的影子。它解决的问题在密码学里其实很朴素:如何让一个人在不暴露自己私钥的前提下&#xff…

2026/10/21 8:59:08 阅读更多 →

日新闻

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

统一内存+量化:锐龙AI Max PRO 400如何本地跑320B大模型

这段时间硬件圈和AI圈都在传一个事:AMD锐龙AI Max PRO 400系列把最高192GB统一内存搬进了PC形态的产品里,按宣传口径,本地跑320B参数的大家伙不再是纸面概念。我第一反应倒不是“内存真大”,而是这话背后的含金量——它把过去只能…

2026/10/21 0:00:11 阅读更多 →
AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

AI助手深度使用体验:对话质量、长文本处理与指令遵循如何留住用户

1. 从“封号重开”这个行为说起:用户到底在追求什么“封号重开”这四个字,放在任何产品语境里都带着一股决绝的味道。一个账号从零开始积累使用记录、调教偏好、沉淀工作流,背后是大量的时间和精力成本。正常情况下,没有人愿意主动…

2026/10/21 0:00:11 阅读更多 →
ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

ROS中YOLOv3工程落地:通信同步、坐标变换与性能调优

1. 为什么在ROS里硬刚YOLOv3不是“炫技”,而是工程落地的必经之路我第一次在某高校机器人实验室看到那台搭载Jetson TX2的移动底盘原地打转、反复识别错垃圾桶时,就意识到:单纯跑通darknet_ros demo和真正让YOLOv3在ROS里“干活”&#xff0c…

2026/10/21 0:00:11 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/21 4:27:17 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →