尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SSM+JSP超市订单系统实战:从搭建到权限导出

SSM+JSP超市订单系统实战:从搭建到权限导出 简介这是一套面向Java Web初学者与课程设计学生的超市订单管理系统实战项目基于SSMSpringSpringMVCMyBatis框架整合MySQL数据库与JSP动态页面技术完整覆盖企业级订单、供应商、用户管理及RBAC权限控制等核心业务场景。资源包共383个文件含74个jar依赖库、40个JSP页面实现前后端交互与数据展示、30个Java源码含Controller、Service、Entity层、38个XML配置文件Spring与MyBatis映射、46个JS脚本增强前端交互及36个PNG图标资源整体压缩包26.2MB结构清晰、模块分明便于逐层理解MVC分层架构与全栈开发流程。已有2568人学习下载提供可直接部署运行的完整工程含SQL建表语句、Tomcat适配配置及典型异常处理逻辑附带编译后的class文件与svn-base元数据支持快速导入IDE调试与二次开发。1. 为什么还在用 JavaSSMMySQLJSP 做超市订单管理系统不是过时而是稳得踏实你可能在招聘网站上刷到过这样的 JD“熟悉 SSM 框架能独立开发基于 JSP 的后台管理系统”也可能在高校课程设计选题表里看到“超市订单管理系统JavaSSMMySQLJSP”被标为“推荐难度中等”。这不是历史遗迹——它仍是中小商贸企业、校园实训、外包快速交付场景下最易落地、调试可见、部署轻量的组合。SSMSpringSpringMVCMyBatis提供清晰的分层控制与事务管理能力MySQL 承担结构化订单、商品、库存、会员四类核心数据的可靠存储JSP 则在无需引入 Vue/React 学习成本的前提下通过c:forEach和sql:query快速渲染订单列表、统计报表、入库单打印页。它不追求高并发吞吐但要求字段级权限控制如采购员不可见销售毛利、操作留痕谁在何时修改了某笔订单状态、导出 Excel 可控格式含合并单元格与表头样式。对刚脱离 JavaSE 阶段的开发者这套技术栈的调试路径极短改完 JSP 页面刷新即见效果打断点查 MyBatis SQL 执行参数看 Tomcat 日志定位 SpringMVC 请求映射失败原因——所有环节都在 IDE 和浏览器之间闭环。2. 搭建可运行的最小 SSMJSP 工程从 Maven 依赖到 DispatcherServlet 初始化2.1 选择兼容版本组合并声明核心依赖SSM 各组件存在隐性版本约束Spring 5.x 与 MyBatis 3.4 兼容良好但若搭配 JDK 17需避开 Spring 4.3.29 以下版本其反射机制不支持新字节码。本项目采用Spring 5.3.32 SpringMVC 5.3.32 MyBatis 3.4.6 MySQL Connector/J 8.0.33 JSTL 1.2组合该组合在 Tomcat 9.0.83 下零报错启动。Mavenpom.xml中关键依赖如下!-- Spring 核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.3.32/version /dependency !-- SpringMVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.3.32/version /dependency !-- MyBatis -- dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.4.6/version /dependency !-- MyBatis-Spring 整合包 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version1.3.2/version /dependency !-- MySQL 驱动8.0 版本必须用 com.mysql.cj.jdbc.Driver-- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency !-- JSTL 支持 JSP 标签库 -- dependency groupIdjstl/groupId artifactIdjstl/artifactId version1.2/version /dependency提示mybatis-spring1.3.2 是适配 MyBatis 3.4.x 的最终稳定版若升级至 MyBatis 3.5.x必须同步升级为mybatis-spring2.0.0否则SqlSessionFactoryBean初始化会抛NoSuchMethodError。2.2 配置 web.xml注册前端控制器与字符编码过滤器JSP 页面提交中文订单备注时若出现乱码根源常在请求未经过滤器预处理。web.xml中需严格按顺序配置CharacterEncodingFilter→HiddenHttpMethodFilter→DispatcherServlet。其中CharacterEncodingFilter必须设置forceRequestEncodingtrue否则 GET 请求参数仍走容器默认编码ISO-8859-1!-- 字符编码过滤器 -- filter filter-nameCharacterEncodingFilter/filter-name filter-classorg.springframework.web.filter.CharacterEncodingFilter/filter-class init-param param-nameencoding/param-name param-valueUTF-8/param-value /init-param init-param param-nameforceRequestEncoding/param-name param-valuetrue/param-value /init-param /filter filter-mapping filter-nameCharacterEncodingFilter/filter-name url-pattern/*/url-pattern /filter-mapping !-- SpringMVC 前端控制器 -- servlet servlet-namedispatcher/servlet-name servlet-classorg.springframework.web.servlet.DispatcherServlet/servlet-class init-param param-namecontextConfigLocation/param-name param-valueclasspath:springmvc.xml/param-value /init-param load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namedispatcher/servlet-name url-pattern//url-pattern /servlet-mapping2.3 编写 springmvc.xml定义处理器映射与视图解析器springmvc.xml是 SpringMVC 的核心配置文件需明确三件事① 扫描Controller类② 配置InternalResourceViewResolver将逻辑视图名转为 JSP 路径③ 开启注解驱动以支持RequestMapping。注意prefix和suffix必须与实际 JSP 存放路径严格匹配!-- 启用注解驱动 -- mvc:annotation-driven / !-- 扫描 Controller 包 -- context:component-scan base-packagecom.supermarket.controller / !-- 视图解析器将 return order/list 解析为 /WEB-INF/jsp/order/list.jsp -- bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/WEB-INF/jsp/ / property namesuffix value.jsp / /bean注意JSP 文件必须放在src/main/webapp/WEB-INF/jsp/目录下非src/main/resources否则 Tomcat 启动时InternalResourceViewResolver无法定位资源报HTTP Status 404 – /WEB-INF/jsp/order/list.jsp not found。3. 订单核心业务实现从数据库建模到 JSP 表单提交的全链路3.1 MySQL 订单相关表设计满足超市业务的最小完备性超市订单系统需支撑“采购入库→销售出库→库存变动→财务结算”闭环。仅order_info单表无法满足需求必须建立关联表。以下是经生产验证的四张核心表含外键与索引表名关键字段说明goodsid(PK),name,unit_price,stock_num,category_id商品主数据stock_num实时库存量order_infoid(PK),order_no,order_type(1采购,2销售),status(0待审核,1已完成),create_time订单头信息order_no为业务单号如 CG20240520001order_detailid(PK),order_id(FK),goods_id(FK),quantity,unit_price,amount订单明细amount quantity × unit_priceuser_infoid(PK),username,role(1管理员,2采购员,3销售员)用户角色控制基础权限-- 创建 goods 表带库存检查索引 CREATE TABLE goods ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, unit_price DECIMAL(10,2) NOT NULL DEFAULT 0.00, stock_num INT NOT NULL DEFAULT 0, category_id INT NOT NULL ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建 order_info 表带状态与时间索引 CREATE TABLE order_info ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, order_type TINYINT NOT NULL COMMENT 1:采购,2:销售, status TINYINT NOT NULL DEFAULT 0 COMMENT 0:待审核,1:已完成, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_status_type (status, order_type), INDEX idx_create_time (create_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 创建 order_detail 表外键约束保证数据一致性 ALTER TABLE order_detail ADD CONSTRAINT fk_order_detail_order FOREIGN KEY (order_id) REFERENCES order_info(id) ON DELETE CASCADE;提示ON DELETE CASCADE确保删除主订单时自动清理明细避免孤儿记录。但需在 Service 层用Transactional包裹删除操作防止部分删除失败导致数据不一致。3.2 MyBatis 映射订单新增动态 SQL 处理多商品批量插入订单提交时前端 JSP 表单可能包含 N 条商品明细如采购单录入 5 种商品。MyBatis 的foreach标签是处理批量插入的标准方案。OrderMapper.xml中定义!-- 插入订单头并获取自增主键 -- insert idinsertOrderHeader parameterTypeOrderInfo useGeneratedKeystrue keyPropertyid INSERT INTO order_info (order_no, order_type, status, create_time) VALUES (#{orderNo}, #{orderType}, #{status}, NOW()) /insert !-- 批量插入订单明细 -- insert idbatchInsertOrderDetail parameterTypejava.util.List INSERT INTO order_detail (order_id, goods_id, quantity, unit_price, amount) VALUES foreach collectionlist itemdetail separator, (#{detail.orderId}, #{detail.goodsId}, #{detail.quantity}, #{detail.unitPrice}, #{detail.amount}) /foreach /insert对应的 Service 方法需保证事务原子性Transactional public void createOrder(OrderInfo orderInfo, ListOrderDetail details) { // 1. 插入订单头获取生成的 order_id orderMapper.insertOrderHeader(orderInfo); // 2. 将 order_id 设置到每条明细中 for (OrderDetail detail : details) { detail.setOrderId(orderInfo.getId()); } // 3. 批量插入明细 orderMapper.batchInsertOrderDetail(details); }3.3 JSP 页面实现订单提交表单绑定与错误提示一体化JSP 表单需同时满足① 动态生成 N 行商品输入框② 提交后校验失败时保留已填数据③ 显示字段级错误如“商品数量必须大于0”。使用 JSTLc:forEach与 EL 表达式实现!-- /WEB-INF/jsp/order/create.jsp -- form action${pageContext.request.contextPath}/order/save methodpost table tr td订单类型/td td select nameorderType option value1采购订单/option option value2销售订单/option /select /td /tr !-- 动态商品行 -- c:forEach varitem items${orderDetails} varStatusstatus tr td商品ID/td tdinput typetext namedetails[${status.index}].goodsId value${item.goodsId} //td /tr tr td数量/td tdinput typenumber namedetails[${status.index}].quantity value${item.quantity} min1 //td /tr /c:forEach /table !-- 错误提示区 -- c:if test${not empty errorMsg} div stylecolor:red;${errorMsg}/div /c:if input typesubmit value提交订单 / /formController 接收时使用ModelAttribute绑定嵌套对象RequestMapping(value /save, method RequestMethod.POST) public String saveOrder(ModelAttribute OrderInfo orderInfo, RequestParam(details[0].goodsId) String[] goodsIds, Model model) { try { // 构建 details 列表... orderService.createOrder(orderInfo, details); return redirect:/order/list; } catch (IllegalArgumentException e) { model.addAttribute(errorMsg, e.getMessage()); model.addAttribute(orderDetails, orderInfo.getDetails()); // 保留原数据 return order/create; } }4. 权限控制与数据导出基于角色的菜单过滤与 Excel 生成实战4.1 JSP 页面级权限控制用自定义标签库隐藏敏感操作按钮超市系统中采购员不应看到“销售退货”按钮销售员不可操作“供应商管理”。Spring Security 过重而 JSP 自定义标签库Tag Library轻量且直观。创建auth.tld文件声明标签?xml version1.0 encodingUTF-8? taglib xmlnshttp://java.sun.com/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd version2.1 tlib-version1.0/tlib-version short-nameauth/short-name uri/auth/uri tag namehasRole/name tag-classcom.supermarket.tag.HasRoleTag/tag-class body-contentJSP/body-content attribute namerole/name requiredtrue/required rtexprvaluetrue/rtexprvalue /attribute /tag /taglibHasRoleTag.java从 Session 获取用户角色并比对public class HasRoleTag extends TagSupport { private String role; Override public int doStartTag() throws JspException { HttpSession session pageContext.getSession(); UserInfo user (UserInfo) session.getAttribute(loginUser); if (user ! null user.getRole().equals(Integer.parseInt(role))) { return EVAL_BODY_INCLUDE; // 渲染标签体 } return SKIP_BODY; // 不渲染 } }JSP 中使用% taglib prefixauth uri/auth % !-- 仅管理员可见 -- auth:hasRole role1 button onclicklocation.href${pageContext.request.contextPath}/admin/userManage 用户管理 /button /auth:hasRole !-- 仅采购员可见 -- auth:hasRole role2 button onclicklocation.href${pageContext.request.contextPath}/purchase/supplierList 供应商管理 /button /auth:hasRole4.2 JSP 导出订单为 ExcelApache POI 4.1.2 生成带样式的 .xlsx 文件JSP 直接调用 POI 生成 Excel 是常见需求但需规避response.getOutputStream()与 JSP 输出流冲突。正确做法是Controller 返回void手动设置响应头并写入OutputStreamRequestMapping(/export) public void exportOrders(HttpServletResponse response) throws IOException { ListOrderInfo orders orderService.listAllOrders(); // 创建工作簿 XSSFWorkbook workbook new XSSFWorkbook(); XSSFSheet sheet workbook.createSheet(订单列表); // 设置表头样式 XSSFCellStyle headerStyle workbook.createCellStyle(); Font font workbook.createFont(); font.setBold(true); headerStyle.setFont(font); // 写入表头 String[] headers {订单号, 类型, 状态, 创建时间, 总金额}; Row headerRow sheet.createRow(0); for (int i 0; i headers.length; i) { Cell cell headerRow.createCell(i); cell.setCellValue(headers[i]); cell.setCellStyle(headerStyle); } // 写入数据行 for (int i 0; i orders.size(); i) { Row row sheet.createRow(i 1); OrderInfo order orders.get(i); row.createCell(0).setCellValue(order.getOrderNo()); row.createCell(1).setCellValue(order.getOrderType() 1 ? 采购 : 销售); row.createCell(2).setCellValue(order.getStatus() 1 ? 已完成 : 待审核); row.createCell(3).setCellValue(new SimpleDateFormat(yyyy-MM-dd HH:mm:ss) .format(order.getCreateTime())); row.createCell(4).setCellValue(order.getTotalAmount()); } // 设置响应头触发下载 response.setContentType(application/vnd.openxmlformats-officedocument.spreadsheetml.sheet); response.setHeader(Content-Disposition, attachment; filenameorders_ System.currentTimeMillis() .xlsx); workbook.write(response.getOutputStream()); workbook.close(); }注意POI 4.1.2 要求 JDK 8且XSSFWorkbook生成.xlsx非.xls避免旧版 Excel 兼容问题。若需合并单元格或设置列宽调用sheet.addMergedRegion()与sheet.setColumnWidth()即可。5. 生产环境关键调优与排错Tomcat 配置、SQL 性能瓶颈与 JSP 编译路径5.1 Tomcat 优化调整线程池与 JSP 编译参数防热加载失效默认 Tomcat 的maxThreads200对超市后台足够但需显式配置connectionTimeout防止慢查询拖垮连接池。server.xml中修改ConnectorConnector port8080 protocolHTTP/1.1 connectionTimeout20000 redirectPort8443 maxThreads150 minSpareThreads25 maxSpareThreads75 acceptCount100 /JSP 修改后需重新编译但默认developmenttrue会导致频繁扫描.jsp文件。生产环境应关闭热加载在web.xml中添加jsp-config jsp-property-group url-pattern*.jsp/url-pattern init-param param-namedevelopment/param-name param-valuefalse/param-value !-- 关闭热编译 -- /init-param init-param param-namefork/param-name param-valuefalse/param-value /init-param /jsp-property-group /jsp-config编译后的.class文件默认存于work/Catalina/localhost/[app-name]/org/apache/jsp/可通过CATALINA_BASE/conf/catalina.properties修改tomcat.work.dir路径。5.2 MySQL 查询性能诊断针对订单列表页的慢 SQL 优化订单列表页执行SELECT * FROM order_info o JOIN order_detail d ON o.idd.order_id时若未加索引百万级订单下响应超 5 秒。使用EXPLAIN分析EXPLAIN SELECT o.order_no, o.status, d.quantity, d.amount FROM order_info o JOIN order_detail d ON o.id d.order_id WHERE o.create_time 2024-01-01;结果若显示typeALL全表扫描则需添加复合索引-- 在 order_detail 表上创建 (order_id, quantity, amount) 覆盖索引 ALTER TABLE order_detail ADD INDEX idx_order_detail_cover (order_id, quantity, amount); -- 在 order_info 表上创建 (create_time, status) 索引加速时间范围查询 ALTER TABLE order_info ADD INDEX idx_create_time_status (create_time, status);提示覆盖索引Covering Index使查询直接从索引获取全部字段避免回表。idx_order_detail_cover可让SELECT quantity, amount FROM order_detail WHERE order_id?完全走索引。5.3 JSP 页面常见 500 错误定位从堆栈日志到 EL 表达式空指针当 JSP 报javax.el.PropertyNotFoundException: Property xxx not found on type xxx本质是 EL 表达式访问了 null 对象的属性。例如${order.user.username}中order.user为 null。解决方案有二JSP 中防御性判断c:if test${not empty order.user} ${order.user.username} /c:ifController 中确保对象不为空RequestMapping(/detail/{id}) public String orderDetail(PathVariable Integer id, Model model) { OrderInfo order orderService.findById(id); // 若 user 为 null设默认值避免 EL 报错 if (order.getUser() null) { order.setUser(new UserInfo()); // 空对象 } model.addAttribute(order, order); return order/detail; }Tomcat 日志中定位此类错误的关键线索是org.apache.jasper.runtime.JspFactoryImpl开头的堆栈而非 Spring 的DispatcherServlet表明问题发生在 JSP 渲染阶段与后端 Controller 逻辑无关。本文还有配套的精品资源点击获取
返回列表