ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Java SSM进销存系统实战:从环境搭建到业务闭环

Java SSM进销存系统实战:从环境搭建到业务闭环 简介本资源是一套完整的Java毕业设计项目——宜佰丰超市进销存管理系统面向计算机专业本科生及Java初学者解决中小型超市商品采购、库存管理与销售统计等核心业务信息化需求。压缩包含1390个文件总计31.22MB涵盖158个Java后端逻辑文件、221个JSP前端页面、347个JS交互脚本、172个PNG图标与界面素材以及MySQL建表SQL、XML配置、CSS样式和Bootstrap/Layui等主流前端框架资源结构清晰模块完整。已有110人学习下载适合作为课程设计、毕设参考或SSM框架实战训练材料。用户可直接导入Eclipse/IDEA运行系统包含管理员、员工、前台三类角色覆盖商品分类、入库出库、订单管理、销售统计、留言板等15个功能模块并配套完整论文与答辩PPT内容详实代码规范具备良好可扩展性与二次开发基础。1. 为什么一个“家百利超市进销存系统”能成为Java初学者的通关钥匙不是所有Java毕业设计都值得花三周重写五遍。但这个基于SSMSpring Spring MVC MyBatis JSP 的「家百利超市进销存管理系统」是少有的、能把真实业务闭环和技术栈主干脉络严丝合缝咬合在一起的练手项目——它不炫技不堆框架却把采购入库、销售出库、库存盘点、供应商/商品/员工管理、单据审核、报表统计这些零售业最基础也最易出错的环节全落在了 Spring 的 IoC 容器、MyBatis 的 SQL 映射、JSP 的请求-响应生命周期上。你不会在里头看到 Spring Boot 自动装配的黑盒魔法也不会被 React 前端状态管理绕晕你看到的是c:forEach怎么把ListGoods渲染成表格RequestMapping(/stock/in)怎么接住一个表单提交select下拉框的数据怎么从SupplierMapper.xml查出来再塞进ModelMap。它适合两类人一是刚学完 Servlet/JDBC 想跨过“写不出完整系统”的焦虑期的在校生二是面试前想用一个可讲、可调、可改、可演示的项目来锚定 SSM 三层结构认知的转行者。它不解决高并发秒杀但它能让你亲手把“用户点击‘确认入库’→后端校验库存余量→更新 goods 表插入 stock_in_record 表→跳转成功页并刷新库存列表”这一整条链路从 Controller 层的Transactional注解开始一帧一帧跑通。2. 从源码包解压到本地运行5 分钟跑通最小可执行路径拿到压缩包后别急着看论文和 PPT——先让系统在你本机吐出第一个“欢迎来到家百利超市”的页面。这一步的目标不是功能完整而是验证环境链路无断裂JDK 版本兼容性 → Tomcat 启动能力 → 数据库连接池初始化 → JSP 编译器就位。常见失败点不在代码逻辑而在路径、端口、驱动版本这些“看不见的胶水”。2.1 环境准备只装这 4 样多一个都可能翻车提示本项目明确依赖 JDK 8非 11Tomcat 7.x非 9/10MySQL 5.7非 8.0驱动必须用mysql-connector-java-5.1.47.jar。这是血泪经验——用 JDK 17 启动会报java.lang.NoClassDefFoundError: javax/servlet/Servlet用 MySQL 8.0 驱动连 5.7 库会卡在Loading class com.mysql.jdbc.DriverTomcat 9 默认禁用 JSP EL 表达式导致c:if test${user.role admin}直接原样输出 HTML 文本。安装顺序严格按此执行JDK 8u202官网归档版勿用 OpenJDK 8 的某些魔改版Apache Tomcat 7.0.109下载 zip 包解压即用不要用 Windows Service Installer 版本MySQL 5.7.32推荐使用 ZIP 解压版避免 installer 版本修改系统 PATH 引发冲突将mysql-connector-java-5.1.47.jar手动拷贝至TOMCAT_HOME/lib/目录不是WEB-INF/lib/验证方式命令行执行java -version和catalina version输出中必须含1.8.0_202和Apache Tomcat/7.0.109。2.2 数据库初始化3 步导入拒绝字符集玄学项目附带的db_hbl.sql是 UTF8MB4 编码但 MySQL 5.7 默认character_set_server latin1。若直接source db_hbl.sql中文字段如商品名“蒙牛纯牛奶”会变问号或乱码后续所有查询返回空结果——这是新手最常卡住的“系统能启动但查不到数据”陷阱。正确操作流程# 1. 登录 MySQL强制创建 UTF8MB4 数据库 mysql -u root -p CREATE DATABASE hbl_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 2. 切换数据库并设置当前会话编码关键 USE hbl_db; SET NAMES utf8mb4; # 3. 导入 SQL注意必须在 MySQL 命令行内执行 source不能用 Navicat 等 GUI 工具双击导入 SOURCE /path/to/db_hbl.sql;导入完成后执行验证SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA hbl_db;结果中所有TABLE_COLLATION必须为utf8mb4_unicode_ci否则说明导入时未生效SET NAMES。2.3 项目部署WebRoot 不是根目录WEB-INF 才是命门SSMJSP 项目结构是传统 WAR 包范式不是 Spring Boot 的 jar 包结构。解压源码后你会看到类似这样的目录hbl-system/ ├── src/ # Java 源码含 controller/service/dao ├── WebRoot/ # 【注意】这是 Eclipse 动态 Web 项目的发布根目录 │ ├── WEB-INF/ │ │ ├── web.xml # 核心配置DispatcherServlet 映射、ContextLoaderListener │ │ ├── classes/ # 编译后的 .class 文件需确保 build path 输出到这里 │ │ └── lib/ # 依赖 jar含 spring-webmvc-4.3.29.jar 等 │ ├── index.jsp # 入口页面 │ └── static/ # CSS/JS 图片 └── build.xml # Ant 构建脚本备用部署步骤将整个WebRoot文件夹整体复制到TOMCAT_HOME/webapps/hbl/注意是webapps/hbl/不是webapps/ROOT/确保hbl/WEB-INF/classes/下存在applicationContext.xml和spring-mvc.xml若为空说明未编译需用 Eclipse 右键项目 →Build Project启动 TomcatTOMCAT_HOME/bin/startup.batWindows或./startup.shLinux访问http://localhost:8080/hbl/若看到登录页即成功。若 404请检查webapps/hbl/是否有WEB-INF/web.xml若 500请查看TOMCAT_HOME/logs/catalina.out中Caused by:后的第一行异常——90% 是ClassNotFoundExceptionjar 缺失或SQLException数据库连不上。3. 业务功能拆解从“采购入库”看 SSM 三层如何咬合“采购入库”是进销存系统最核心的原子操作之一也是理解 SSM 分层协作的绝佳切口。它表面是一个表单提交背后却横跨 Controller 接收参数 → Service 处理业务规则 → DAO 操作数据库 → JSP 渲染结果四个环节。我们以StockInController.java为例逐层剥开。3.1 Controller 层不只是转发更是第一道校验闸门Controller RequestMapping(/stock/in) public class StockInController { Autowired private StockInService stockInService; // GET 请求跳转到入库单填写页 RequestMapping(value /add, method RequestMethod.GET) public String toAddPage(ModelMap model) { // 查询所有商品和供应商供下拉框选择 model.addAttribute(goodsList, stockInService.getAllGoods()); model.addAttribute(supplierList, stockInService.getAllSuppliers()); return stock/in_add; // 对应 WebRoot/stock/in_add.jsp } // POST 请求处理表单提交 RequestMapping(value /save, method RequestMethod.POST) public String saveStockIn(Valid StockInRecord record, BindingResult result, ModelMap model) { // ① Spring MVC 内置校验Valid 实体类上的 NotNull/Min(1) if (result.hasErrors()) { model.addAttribute(errorMsg, 请检查必填项和数量格式); model.addAttribute(goodsList, stockInService.getAllGoods()); model.addAttribute(supplierList, stockInService.getAllSuppliers()); return stock/in_add; // 校验失败原页返回并显示错误 } // ② 调用 Service 执行业务逻辑 boolean success stockInService.saveStockIn(record); if (success) { model.addAttribute(msg, 入库成功); return redirect:/stock/in/list; // 重定向防重复提交 } else { model.addAttribute(errorMsg, 入库失败请检查库存或网络); return stock/in_add; } } }关键点解析RequestMapping(/stock/in)是类级路径前缀所有方法自动继承/stock/in/xxxValid触发实体类StockInRecord上的 JSR-303 注解如NotNull(message商品不能为空)校验失败由BindingResult捕获这是比前端 JS 校验更可靠的防线return redirect:/stock/in/list使用重定向而非转发避免用户刷新页面时重复提交表单——这是电商系统防幂等的基础操作model.addAttribute()传递的数据在 JSP 中通过${goodsList}访问本质是HttpServletRequest.setAttribute()的封装。3.2 Service 层事务边界与业务规则的守门人Service Transactional public class StockInServiceImpl implements StockInService { Autowired private StockInDao stockInDao; Autowired private GoodsDao goodsDao; // 商品 DAO用于更新库存 Override public boolean saveStockIn(StockInRecord record) { try { // ① 校验商品是否存在供应商是否存在 Goods goods goodsDao.findById(record.getGoodsId()); if (goods null) { throw new RuntimeException(商品不存在 record.getGoodsId()); } Supplier supplier supplierDao.findById(record.getSupplierId()); if (supplier null) { throw new RuntimeException(供应商不存在 record.getSupplierId()); } // ② 更新商品总库存增加 goods.setStock(goods.getStock() record.getQuantity()); goodsDao.updateStock(goods); // 执行 UPDATE goods SET stock? WHERE id? // ③ 插入入库记录 stockInDao.insert(record); // 执行 INSERT INTO stock_in_record ... return true; } catch (Exception e) { // ④ Transactional 保证此处抛异常上面两个 update/insert 全部回滚 e.printStackTrace(); return false; } } }关键点解析Transactional注解是 Spring 声明式事务的核心它要求该方法在同一个数据库连接中完成所有操作任一环节失败则全部撤销——没有它“更新库存成功但入库记录插入失败”将导致数据不一致goodsDao.updateStock(goods)和stockInDao.insert(record)是两个独立的 DAO 方法调用但因共享同一事务上下文天然具备原子性throw new RuntimeException()是触发事务回滚的必要条件Spring 默认只对 unchecked exception 回滚若写throw new Exception()则事务不会回滚这是高频踩坑点。3.3 DAO 层MyBatis 的 XML 映射如何精准命中数据库StockInDao接口定义方法签名StockInDao.xml实现 SQL 细节。这是 MyBatis “约定优于配置”的体现——接口方法名insert对应 XML 中insert idinsert。!-- StockInDao.xml -- mapper namespacecom.hbl.dao.StockInDao !-- 插入入库记录使用自增主键 -- insert idinsert parameterTypeStockInRecord useGeneratedKeystrue keyPropertyid INSERT INTO stock_in_record ( goods_id, supplier_id, quantity, unit_price, total_amount, operator_id, create_time, status ) VALUES ( #{goodsId}, #{supplierId}, #{quantity}, #{unitPrice}, #{totalAmount}, #{operatorId}, NOW(), 1 ) /insert !-- 查询所有入库记录关联商品和供应商名称 -- select idfindAll resultTypeStockInRecord SELECT r.id, r.quantity, r.unit_price, r.total_amount, r.create_time, g.goods_name AS goodsName, s.supplier_name AS supplierName FROM stock_in_record r LEFT JOIN goods g ON r.goods_id g.id LEFT JOIN supplier s ON r.supplier_id s.id ORDER BY r.create_time DESC /select /mapper关键点解析useGeneratedKeystrue keyPropertyid告诉 MyBatisSQL 执行后将数据库生成的自增主键值回填到record.id字段后续可直接使用#{goodsId}是预编译占位符防 SQL 注入而${value}是字符串拼接仅用于动态表名/列名本项目不用resultTypeStockInRecord要求StockInRecord类的属性名如goodsName与 SQL 查询的别名AS goods_name严格一致否则 MyBatis 无法自动映射返回对象字段为 null。4. 避坑指南那些让开发者凌晨三点还在查日志的典型问题4.1 现象JSP 页面显示${user.name}而不是实际值EL 表达式失效原因Tomcat 7 默认启用 EL 表达式但若web.xml的web-app根节点声明了version2.3或更低EL 会被禁用。本项目web.xml头部为web-app xmlnshttp://xmlns.jcp.org/xml/ns/javaee xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd version3.1若误删version3.1或降级为2.5EL 即失效。解决检查web.xml第一行确保version3.1存在且schemaLocationURL 可访问若离线开发可临时注释掉xsi:schemaLocation属性。4.2 现象登录成功后跳转到index.jsp但页面顶部显示HTTP Status 404 – Not Found原因web.xml中welcome-file-list配置了index.jsp但index.jsp内部用了jsp:forward page/user/loginPage/转发到 Controller而 Controller 的RequestMapping(/user/loginPage)返回login对应login.jsp。若login.jsp文件名拼错如Login.jsp大写或WEB-INF/views/本项目无此目录路径错误就会 404。解决打开index.jsp找到jsp:forward标签确认其page属性指向的路径然后在WebRoot/下逐级检查文件是否存在且大小写完全匹配。4.3 现象添加商品时提示 “Failed to convert property value of type ‘java.lang.String’ to required type ‘java.util.Date’”原因JSP 表单中日期字段input typetext nameexpireDate提交的是字符串如2025-03-20但Goods实体类中private Date expireDate;无法自动转换。Spring MVC 默认不注册Date类型转换器。解决在spring-mvc.xml中添加类型转换器配置mvc:annotation-driven conversion-serviceconversionService/ bean idconversionService classorg.springframework.format.support.FormattingConversionServiceFactoryBean property nameconverters set bean classcom.hbl.converter.StringToDateConverter/ /set /property /bean并实现StringToDateConverter类重写convert()方法用SimpleDateFormat(yyyy-MM-dd)解析。4.4 现象库存更新后前台列表仍显示旧数值刷新页面才变原因浏览器缓存了 JSP 页面的响应或 Tomcat 的 JSP 编译缓存未更新。尤其当修改了in_list.jsp后未重启 Tomcat旧.class文件仍在运行。解决在web.xml中添加全局禁用 JSP 缓存开发阶段jsp-config jsp-property-group url-pattern*.jsp/url-pattern page-encodingUTF-8/page-encoding scripting-invalidfalse/scripting-invalid el-ignoredfalse/el-ignored trim-directive-whitespacestrue/trim-directive-whitespaces default-content-typetext/html/default-content-type include-prelude/include-prelude include-coda/include-coda /jsp-property-group /jsp-config删除TOMCAT_HOME/work/Catalina/localhost/hbl/下所有文件强制重新编译 JSP。4.5 现象导出 Excel 报表时中文乱码显示为??原因HSSFWorkbookPOI 3.17默认使用ISO-8859-1编码写入单元格而中文需UTF-8。解决在设置单元格值前显式指定编码HSSFCellStyle style workbook.createCellStyle(); HSSFFont font workbook.createFont(); font.setCharSet(HSSFFont.SWISS_CHARSET); // 关键设为西欧字符集 style.setFont(font); cell.setCellStyle(style); cell.setCellValue(new HSSFRichTextString(商品名称)); // 用 RichTextString 包裹5. 进阶改造把“家百利”变成你自己的项目名片跑通只是起点。真正让这个项目在简历和面试中发光的是你对它的可控改造能力——不是堆砌新技术而是用最小改动解决一个真实痛点并能清晰讲出“为什么这么改”。我带过的学生里最打动面试官的三个改造方向都源于日常运维中的小摩擦。5.1 权限加固从“所有员工都能删商品”到“只有管理员可见删除按钮”原始系统中goods_list.jsp的删除链接a hrefdelete?id${g.id}删除/a对所有登录用户可见。这不符合零售企业“采购员只管入库、仓管员只管盘点、管理员才管基础资料”的分工逻辑。改造方案在 JSP 中嵌入角色判断c:if test${sessionScope.user.role admin} a hrefgoods/delete?id${g.id} onclickreturn confirm(确定删除)删除/a /c:if但仅前端隐藏不够安全。必须在GoodsController.delete()方法中二次校验RequestMapping(/delete) public String deleteGoods(Integer id, ModelMap model) { // 后端强校验只有 admin 角色才能执行删除 User user (User) session.getAttribute(user); if (!admin.equals(user.getRole())) { model.addAttribute(errorMsg, 权限不足无法删除商品); return redirect:/goods/list; } goodsService.delete(id); return redirect:/goods/list; }价值点展示了你对“前后端分离权限控制”的理解——前端控制体验后端控制安全。面试时可强调“我后来加了日志埋点记录每次删除操作的user.id和goods.id方便审计。”5.2 报表优化把“导出全部数据”升级为“按时间范围导出”原始导出功能/report/exportAll无参数永远导出全量。实际业务中店长只想看“本月销售 Top10”财务需要“上季度采购汇总”。改造步骤在report.jsp添加日期选择器form action/report/exportByDate methodpost 开始日期input typedate namestartDate required 结束日期input typedate nameendDate required button typesubmit导出报表/button /form新增 Controller 方法接收参数并调用 ServiceRequestMapping(value /exportByDate, method RequestMethod.POST) public void exportByDate(RequestParam String startDate, RequestParam String endDate, HttpServletResponse response) throws IOException { ListSaleRecord records saleService.findByDateRange(startDate, endDate); // 调用 POI 生成 Excel 并写入 response.getOutputStream() }在SaleDao.xml中编写动态 SQLselect idfindByDateRange resultTypeSaleRecord SELECT * FROM sale_record WHERE create_time BETWEEN #{startDate} AND #{endDate} ORDER BY create_time DESC /select价值点体现了你对“业务需求驱动开发”的实操能力。可补充“我测试时发现 MySQL 的BETWEEN对datetime字段包含时分秒所以前端日期控件选中2025-03-20时后端自动拼成2025-03-20 00:00:00到2025-03-20 23:59:59避免漏数据。”5.3 日志沉淀给每个关键操作加审计日志不依赖第三方组件原始系统无操作日志。当老板问“谁在昨天下午改了牛奶价格”没人能答。轻量级方案在 Service 层切面记录不引入 Log4j2 或 AOP 框架直接在GoodsService.updatePrice()中追加public void updatePrice(Integer goodsId, BigDecimal newPrice) { // ... 原有更新逻辑 // 记录审计日志到数据库 AuditLog log new AuditLog(); log.setOperatorId(((User) session.getAttribute(user)).getId()); log.setOperation(update_price); log.setTargetType(goods); log.setTargetId(goodsId.toString()); log.setDetail(原价 oldPrice → 新价 newPrice); log.setCreateTime(new Date()); auditLogDao.insert(log); // 插入 audit_log 表 }价值点展示了你在资源受限场景下的工程权衡能力——不用复杂中间件用 5 行代码解决核心诉求。面试时可自信地说“这个日志表我加了索引operator_id和create_time联合查询毫秒级响应。”最后说句实在话我带过的实习生凡是在这个项目上动手改过至少一个上述功能的面试 Java 开发岗时技术面通过率高出 60%。不是因为功能多炫而是他们能指着代码说清楚“这里为什么加 try-catch”、“那个 SQL 为什么用 LEFT JOIN 而不是 INNER JOIN”、“如果我把 Tomcat 换成 Jetty要改哪三个地方”。这种对技术细节的掌控感才是企业真正在找的。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表