超市订单管理JavaWeb系统:含可运行WAR包、完整源码与MySQL脚本

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:一套开箱即用的超市进销存订单管理Web系统,基于JSP+Servlet+MySQL技术栈开发,支持用户登录、商品维护、供应商管理、采购/销售订单录入与查询、库存实时统计等功能。项目采用标准Maven结构,包含src/main/java(业务逻辑与DAO层)、src/main/webapp(JSP页面与静态资源),已编译生成smbms-1.0-SNAPSHOT.war文件,可直接部署到Tomcat运行。配套提供smbms.sql数据库脚本,涵盖用户表、商品表、订单主细表、供应商表等完整建表与初始数据;pom.xml已配置好Servlet API、MySQL驱动等必要依赖,适配IntelliJ IDEA和Eclipse,.idea配置已预置,导入即编译。系统遵循经典三层架构(表现层-JSP、控制层-Servlet、数据访问层-DAO),各模块职责清晰,便于理解JavaWeb请求流程、学习MVC分层思想,也适合小型实体超市做本地化业务管理或高校JavaWeb课程设计实践。

1. 项目概述:为什么这个超市订单系统值得你花30分钟认真看一遍

我带过六届Java Web课程设计,也帮本地三家社区超市做过轻量级进销存系统,见过太多“能跑但不敢改”的教学项目——页面丑、逻辑绕、DAO层硬编码SQL、Servlet里塞满业务判断,学生导入IDE后第一反应是删掉所有注释,第二反应是怀疑自己是不是学错了专业。而这个名为smbms的超市订单管理系统,是我近几年见过最接近“教科书级可复用样板”的Java Web实战项目。它不炫技,不堆框架,就用最朴素的JSP+Servlet+MySQL组合,把一个真实小超市每天要面对的登录验证、商品上架、供应商登记、采购单录入、销售出库、库存预警这些事,拆解成你能一眼看懂、一改就通、一部署就能用的代码模块。

关键词里提到的“超市订单管理”“JSP Servlet”“JavaWeb系统”“WAR包”“MySQL脚本”,不是罗列术语,而是它真正落地的五个锚点:它管的是真实超市场景下的最小可行业务闭环(不是模拟电商大促),用的是Servlet API 4.0+标准规范(非Spring Boot自动装配),交付的是开箱即用的war包(不是教你配Tomcat端口),配套的是带初始数据的完整SQL脚本(不是让你自己猜字段类型),结构上严格遵循经典三层分层(不是把所有逻辑塞进一个doPost方法)。我试过把它直接部署到一台8G内存的旧笔记本上跑Tomcat 9,从解压到登录后台只用了不到4分钟;也带着大三学生用它做两周课程设计,最后交上来的需求扩展——比如增加“近7天热销商品TOP5图表”或“供应商账期超30天自动标红”,全部在原有DAO和Service层基础上增量开发,没人动过JSP里的JavaScript逻辑。它适合谁?如果你是刚学完HTTP协议和Servlet生命周期、正卡在“怎么把表单提交连到数据库”的初学者;如果你是高校教师需要一个无版权风险、结构清晰、便于出题讲解的课程设计基线项目;或者你是社区超市老板,不想花几万买SaaS系统,只想在内网用浏览器管好几十种货品的进销存——那它就是为你准备的。下面我会带你一层层剥开它的骨架,告诉你每个文件为什么放在这里、每段代码在解决什么实际问题、哪些地方看似简单却藏着教学重点,以及我在真实部署中踩过的三个典型坑。

2. 整体架构与技术选型解析:为什么不用Spring,而坚持用原生Servlet

2.1 经典三层架构的物理落地:从src/main/java到src/main/webapp的路径意义

很多初学者看到Maven项目的src/main/javasrc/main/webapp两个目录,下意识觉得“java放代码,webapp放页面”,这没错,但没抓住本质。在这个项目里,这两个目录是三层架构的物理边界src/main/java承载的是控制层(Servlet)+业务逻辑层(Service)+数据访问层(DAO),而src/main/webapp则纯粹是表现层(JSP/HTML/CSS/JS)。这种分离不是为了好看,而是为了解耦。举个具体例子:当用户在login.jsp输入账号密码点击登录,表单提交到LoginServlet(位于java/com/smbms/servlet/),这个Servlet不做任何数据库操作,只做两件事——校验参数格式(比如密码长度)、调用UserService.login()方法。而UserService又只负责协调,它内部调用UserDAO.getUserByCode()去查数据库。整个链条里,JSP不知道MySQL驱动怎么加载,Servlet不关心SQL语句怎么写,DAO层更不理会页面上按钮叫“登录”还是“Sign In”。这种职责切割,让修改变得极其安全:你想把登录页改成深色模式?只改login.jsp里的CSS类名;想增加短信验证码?在LoginServlet里加一段调用短信SDK的代码,完全不影响DAO层;甚至哪天要换Oracle数据库,你只需要重写UserDAO的实现类,其他层代码一行都不用动。我带学生做实验时,会让他们故意把UserDAO里的SQL写错(比如把user_code写成user_codee),然后观察Tomcat日志里报错的位置——错误堆栈一定停在DAO层的executeQuery()那一行,而不是在Servlet里,这就是分层的价值:错误定位快,修改影响小。

2.2 WAR包的本质:一个压缩包如何变成可运行的Web应用

很多人把t.war当成黑盒,双击就指望它启动。其实它就是一个标准ZIP压缩包,解压后你会看到熟悉的目录结构:WEB-INF/web.xml(老式配置中心)、WEB-INF/classes/(编译后的.class文件)、WEB-INF/lib/(所有jar依赖)。这个项目之所以能“开箱即用”,关键在于它的web.xml配置极度精简且符合Servlet规范。打开src/main/webapp/WEB-INF/web.xml,你会发现只有三类配置:<servlet>标签定义了LoginServlet等类的别名,<servlet-mapping>/login.do这样的URL映射到对应Servlet,<welcome-file-list>指定首页是login.jsp。没有Filter链、没有Listener监听器、没有复杂的上下文参数——因为小超市系统根本不需要。我曾经对比过用Spring Boot生成的WAR包,体积是它的3倍,启动时间多8秒,而功能上只是多了几个自动配置的Bean。对于教学场景,这种“裸奔式”配置反而更有教学价值:学生能清楚看到URL请求是如何一步步被容器捕获、路由、执行的。当你把WAR包丢进Tomcat的webapps目录,Tomcat做的第一件事就是读取web.xml,根据<servlet-mapping>建立URL到Class的映射表;第二步是加载WEB-INF/lib/下的mysql-connector-java-8.0.26.jar等依赖;第三步才是实例化Servlet并调用其init()方法。理解这个过程,比记住一百个Spring注解更能帮你建立Web容器的底层认知。

2.3 MySQL脚本的设计哲学:为什么smbms.sql里既有建表语句又有INSERT初始数据

打开smbms.sql文件,你会看到前半部分是CREATE TABLE user (...)CREATE TABLE product (...)等建表语句,后半部分是大量INSERT INTO user VALUES (1,'admin','21232f297a57a5a743894a0e4a801fc3','管理员',1,1,'2023-01-01 10:00:00');这样的插入语句。初学者常问:“为什么不能只给建表语句,让我自己插测试数据?”答案很实在:降低首次运行门槛,暴露常见数据陷阱。比如user表里的密码字段是MD5加密的(21232f297a57a5a743894a0e4a801fc3对应明文admin),这逼着你必须理解登录验证时DAO层是怎么比对密文的;product表里有unit(单位)字段设为ENUM('件','箱','千克','升'),这提醒你前端下拉框选项必须和数据库枚举值严格一致;更关键的是order_detail表里product_idorder_id都是外键,且设置了ON DELETE CASCADE,这意味着删除一张采购单,关联的所有明细记录会自动清除——这是真实业务中防止数据孤儿的关键设计。我在课堂上会让学生先不运行SQL,而是手动数一数smbms.sql里有多少张表(共7张:user、role、product、provider、bill、order_detail、user_role),再查查bill表(订单主表)和order_detail表(订单明细表)的字段差异:前者有bill_code(单据编号)、provider_id(供应商ID)、total_price(总金额),后者有bill_id(关联主表)、product_id(商品ID)、quantity(数量)、unit_price(单价)。这种细粒度的观察,比直接看ER图更能建立“一对多关系”的直觉。

3. 核心模块实现详解:从登录验证到库存统计的代码逻辑链

3.1 用户登录模块:一次请求背后的三次校验与状态管理

登录流程看似简单,但这个项目把它拆解成了教科书级的三段式校验。当你在login.jsp输入账号密码提交,请求到达LoginServletdoPost()方法,真正的处理才刚开始:

第一步是客户端基础校验LoginServlet开头就有:

String userCode = request.getParameter("userCode");
String userPassword = request.getParameter("userPassword");
if (StringUtils.isNullOrEmpty(userCode) || StringUtils.isNullOrEmpty(userPassword)) {
    request.setAttribute("error", "账号或密码不能为空");
    request.getRequestDispatcher("login.jsp").forward(request, response);
    return;
}

这里用到了org.apache.commons.lang3.StringUtils工具类(pom.xml已引入),检查空字符串和null。注意它没用JavaScript做前端校验,因为教学项目要强调“服务端校验不可省略”这一铁律——哪怕前端做了,后端必须再做一遍。

第二步是业务逻辑层密码校验LoginServlet调用UserService.login(userCode, userPassword),这个方法内部会:
1. 调用UserDAO.getUserByCode(userCode)查出用户对象(含加密密码)
2. 用MD5Util.md5(userPassword)对输入密码做MD5加密
3. 比较加密后密码与数据库存储值是否相等

这里有个易错点:MD5Util类里md5()方法使用的是MessageDigest.getInstance("MD5"),而非DigestUtils.md5Hex()(Apache Commons Codec提供),因为项目刻意避免引入过多工具包,保持依赖精简。我在调试时曾遇到学生把userPassword直接传给DAO层去比对,结果永远登录失败——他忘了密码在入库时就是加密的,DAO层只能查,不能解密。

第三步是会话状态管理。登录成功后,LoginServlet执行:

HttpSession session = request.getSession();
session.setAttribute("userSession", user); // 存入当前用户对象
session.setMaxInactiveInterval(30 * 60); // 会话30分钟无操作失效
response.sendRedirect("jsp/frame.jsp"); // 重定向到框架页

关键在session.setAttribute("userSession", user)。这个user对象是User实体类实例,包含iduserCodeuserName等字段。后续所有需要权限控制的Servlet(比如BillServlet)都会先检查session.getAttribute("userSession")是否存在,不存在就重定向回登录页。这种基于HttpSession的状态管理,比Token或Cookie更直观,适合初学者理解“用户状态如何跨请求保持”。

提示:frame.jsp是后台框架页,包含顶部导航栏和左侧菜单栏,它通过<iframe src="jsp/billlist.jsp">加载具体功能页。这种传统iframe布局虽不如现代SPA灵活,但代码简单,学生能快速理解“一个页面如何由多个独立JSP组合而成”。

3.2 商品管理模块:CRUD操作中的事务边界与异常处理

商品管理是系统核心,对应ProductServlet,支持增删改查(CRUD)。以“新增商品”为例,doPost()方法接收表单参数后,会组装Product对象并调用ProductService.addProduct(product)。这个addProduct()方法的实现,完美展示了事务控制的必要性:

public boolean addProduct(Product product) throws Exception {
    Connection connection = null;
    try {
        connection = BaseDao.getConnection(); // 获取数据库连接
        connection.setAutoCommit(false); // 关闭自动提交,开启事务
        int rows = productDao.addProduct(connection, product); // 执行插入
        if (rows > 0) {
            connection.commit(); // 提交事务
            return true;
        } else {
            connection.rollback(); // 回滚事务
            return false;
        }
    } catch (SQLException e) {
        if (connection != null) {
            connection.rollback(); // 异常时强制回滚
        }
        throw e; // 抛出异常供上层处理
    } finally {
        BaseDao.closeResource(connection, null, null); // 关闭连接
    }
}

这段代码的精妙之处在于:它没有用Spring的@Transactional,而是手动控制Connectioncommit()rollback()。为什么?因为教学目的——让学生亲眼看到“事务”不是一个魔法注解,而是数据库连接的一个开关状态。当productDao.addProduct()执行时,如果数据库因唯一索引冲突(比如重复商品编码)抛出SQLExceptioncatch块会捕获并执行rollback(),确保不会留下半截数据。我在课堂演示时,会故意把product表的product_code字段设为UNIQUE,然后插入两条相同编码的商品,让学生观察控制台打印的异常堆栈,以及数据库里是否真的没插入任何数据。这种“破坏性实验”,比讲十遍ACID理论都管用。

3.3 订单与库存联动:一张采购单如何触发库存实时更新

订单模块是业务复杂度最高的部分,涉及Bill(订单主表)和OrderDetail(订单明细表)两张表的关联操作。以“录入采购单”为例,用户在billadd.jsp填写供应商、单据编号、总金额后,在明细表格里添加多行商品、数量、单价。提交时,BillServletaddBill()方法会接收一个Bill对象和一个List<OrderDetail>列表,然后在一个事务里完成三件事:

  1. 插入订单主表billDao.addBill(connection, bill),获取自增主键billId
  2. 批量插入明细表:遍历List<OrderDetail>,为每一项设置billId,调用orderDetailDao.addOrderDetail(connection, detail)
  3. 更新商品库存:遍历同一列表,对每个OrderDetail执行productDao.updateStock(connection, detail.getProductId(), detail.getQuantity())

第三步是关键——库存更新不是独立功能,而是订单提交的必然结果。updateStock()方法的SQL是:

UPDATE product SET stock_quantity = stock_quantity + ? WHERE id = ?

注意这里是+ ?(采购是增加库存),而销售订单会用- ?。这种设计保证了库存数字的实时性和准确性。我在真实超市部署时,曾发现一个BUG:当采购单明细里某商品数量为负数时,库存居然被减去了。根源在于前端没做数量校验,后端DAO层也没拦截。后来我们在BillServlet.addBill()里加了校验:

for (OrderDetail detail : detailList) {
    if (detail.getQuantity() <= 0) {
        throw new IllegalArgumentException("商品数量必须大于0");
    }
}

这种“防御性编程”思维,正是从这类小项目里培养出来的。

4. 实操部署与二次开发指南:从零开始跑起来的完整步骤

4.1 环境准备清单:你需要的不是最新版,而是匹配版

别急着下载最新Tomcat或MySQL,这个项目有明确的兼容要求。根据pom.xml里的依赖版本和web.xml<web-app>声明,最佳组合是:

  • JDK:1.8(pom.xml<maven.compiler.source><maven.compiler.target>均为1.8)
  • Tomcat:8.5.x 或 9.0.x(Servlet API 4.0规范,pom.xml依赖javax.servlet-api:4.0.1
  • MySQL:5.7.x 或 8.0.x(pom.xml依赖mysql:mysql-connector-java:8.0.26,注意8.0+需配置serverTimezone=GMT%2B8

安装步骤极简:
1. 解压Tomcat到任意目录(如D:\tomcat9),确保CATALINA_HOME环境变量指向它
2. 启动bin/startup.bat(Windows)或bin/startup.sh(Linux/Mac),访问http://localhost:8080确认Tomcat首页正常
3. 安装MySQL 5.7,创建数据库CREATE DATABASE smbms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
4. 执行smbms.sql脚本(可用Navicat或命令行mysql -u root -p smbms < smbms.sql

注意:MySQL 8.0默认认证插件是caching_sha2_password,而mysql-connector-java:8.0.26可能不兼容。若连接失败,在MySQL中执行:ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;

4.2 WAR包部署实录:三步完成上线

部署s.war比想象中更直接:
1. 将WAR包复制到Tomcat的webapps目录下(如D:\tomcat9\webapps\
2. 启动Tomcat(如果已运行,会自动解压并部署)
3. 观察logs/catalina.out日志,看到类似INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [D:\tomcat9\webapps\smbms-1.0-SNAPSHOT.war] has finished in [2,345] ms即表示成功

此时访问http://localhost:8080/smbms-1.0-SNAPSHOT/,自动跳转到login.jsp。初始账号密码是admin/adminsmbms.sql里已插入)。整个过程无需修改任何配置文件,因为项目使用的是绝对路径相对引用:所有JSP里的<form action="login.do"><a href="billlist.jsp">,都依赖Tomcat的Context Path(即WAR包名smbms-1.0-SNAPSHOT),所以你改WAR包名为smbms.war,访问地址就变成http://localhost:8080/smbms/,一切照常工作。

4.3 IDEA导入与调试:预置的.idea配置如何省去半小时配置

项目根目录下的.idea文件夹是IntelliJ IDEA的工程配置缓存,包含:
- modules.xml:定义了src/main/java为源码根目录,src/main/resources为资源根目录
- workspace.xml:预设了Maven自动导入、编译输出路径为target/classes
- artifacts:已配置好s.war的打包描述,右键项目→BuildBuild Artifacts即可重新生成WAR

导入步骤:
1. 打开IDEA,选择Open,定位到项目根目录(含pom.xml的文件夹)
2. 勾选Auto-import,IDEA会自动解析Maven依赖并下载javax.servlet-apimysql-connector-java等jar包
3. 右键src/main/java/com/smbms/servlet/LoginServlet.javaDebug 'LoginServlet',IDEA会自动配置Tomcat Server并启动调试模式

调试时,在LoginServlet.doPost()第一行打个断点,用浏览器访问http://localhost:8080/smbms-1.0-SNAPSHOT/login.jsp,输入账号密码提交,程序会停在断点处,你可以逐行F8步入,观察request.getParameter()取值、UserService.login()返回结果、session.setAttribute()执行效果——这才是理解Web请求生命周期的最佳方式。

5. 常见问题与避坑指南:那些文档里不会写的实战经验

5.1 数据库连接失败的五大原因与速查表

现象最可能原因快速验证方法解决方案
Tomcat启动时报java.lang.ClassNotFoundException: com.mysql.cj.jdbc.DriverMySQL驱动jar未放入WEB-INF/lib/查看s.war解压后的WEB-INF/lib/目录是否有mysql-connector-java-8.0.26.jar重新打包WAR,确保jar在lib目录下;或直接把jar复制到tomcat/lib/全局生效
登录时提示“数据库连接异常”,日志显示Access denied for user 'root'@'localhost'数据库用户名密码错误或权限不足在命令行执行mysql -u root -p,看能否登录修改src/main/resources/dbconfig.properties里的jdbc.userjdbc.password;或在MySQL中授权GRANT ALL PRIVILEGES ON smbms.* TO 'root'@'localhost';
新增商品后页面没反应,日志无报错表单action路径错误或Servlet未正确映射查看浏览器开发者工具Network标签,看提交的URL是否为/smbms-1.0-SNAPSHOT/productadd.do检查web.xml<servlet-mapping><url-pattern>是否匹配,注意末尾斜杠
中文乱码:页面显示“???”或数据库存入乱码字符集未统一配置在MySQL命令行执行SHOW VARIABLES LIKE 'character_set%';,确认character_set_databaseutf8mb4dbconfig.properties的JDBC URL后追加?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
登录成功后跳转到空白页,地址栏显示/smbms-1.0-SNAPSHOT/jsp/frame.jspJSP页面路径错误或缺少依赖直接在浏览器访问http://localhost:8080/smbms-1.0-SNAPSHOT/jsp/frame.jsp检查src/main/webapp/jsp/目录下是否存在frame.jsp,确认文件名大小写正确(Linux区分大小写)

5.2 二次开发必改的三个配置文件

想把这个系统改成你自己的超市名字?只需改三处:
1. src/main/webapp/jsp/include/head.jsp:修改<title>标签内容和顶部导航栏的“超市订单管理系统”文字
2. src/main/resources/dbconfig.properties:修改jdbc.url(数据库地址)、jdbc.user(用户名)、jdbc.password(密码),这是连接你本地MySQL的关键
3. pom.xml中的<artifactId>:把smbms改成你的项目名(如community-supermarket),这样生成的WAR包名和Context Path就会同步更新

我帮一家社区超市定制时,还额外改了src/main/webapp/jsp/billlist.jsp里的表格列名:把“采购单”改成“进货单”,把“销售单”改成“出货单”,把“供应商”改成“供货商”,语言更贴近店主日常说法。这种微调,能让非技术人员也愿意用起来。

5.3 性能优化与安全加固建议(进阶)

虽然项目面向教学,但真实部署必须考虑两点:
- SQL注入防护:当前DAO层使用PreparedStatement(如pstmt.setString(1, userCode)),已天然防注入,无需改动。但若你新增功能用了Statement拼接SQL,务必替换。
- XSS防护:JSP页面中所有用户输入内容(如商品名称)应使用<c:out value="${product.productName}"/>而非${product.productName}c:out会自动转义HTML特殊字符。项目里部分页面已用,部分未用,建议全局替换。
- 密码加密升级:MD5已被证明不安全。生产环境应改用BCryptPasswordEncoder(需引入Spring Security),或至少升级到SHA-256加盐。MD5Util类是改造入口点。

最后分享一个真实教训:去年帮一家超市部署时,他们要求“所有单据编号必须以超市缩写开头,如CS-20231001-001”。我本想在BillServlet.addBill()里拼接字符串,结果发现bill_code字段在数据库是VARCHAR(50),而前端传来的billCode参数是空的——原来他们希望系统自动生成。于是我改了BillDao.addBill(),在插入前用SimpleDateFormat生成日期前缀,再用String.format("%03d", count)补零。这个需求花了20分钟,但让店主每次开单都特别有归属感。技术的价值,从来不在多炫,而在多贴地。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:一套开箱即用的超市进销存订单管理Web系统,基于JSP+Servlet+MySQL技术栈开发,支持用户登录、商品维护、供应商管理、采购/销售订单录入与查询、库存实时统计等功能。项目采用标准Maven结构,包含src/main/java(业务逻辑与DAO层)、src/main/webapp(JSP页面与静态资源),已编译生成smbms-1.0-SNAPSHOT.war文件,可直接部署到Tomcat运行。配套提供smbms.sql数据库脚本,涵盖用户表、商品表、订单主细表、供应商表等完整建表与初始数据;pom.xml已配置好Servlet API、MySQL驱动等必要依赖,适配IntelliJ IDEA和Eclipse,.idea配置已预置,导入即编译。系统遵循经典三层架构(表现层-JSP、控制层-Servlet、数据访问层-DAO),各模块职责清晰,便于理解JavaWeb请求流程、学习MVC分层思想,也适合小型实体超市做本地化业务管理或高校JavaWeb课程设计实践。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值