ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

SSM框架电脑配件销售系统设计与实现要点解析

SSM框架电脑配件销售系统设计与实现要点解析 简介这是一份基于SSM框架开发的电脑配件销售系统完整源码包面向Java学习者、毕业设计学生及需要快速搭建B/S商城项目的开发者。系统采用SpringSpringMVCMyBatisMavenMySQL实现前端以JSP、CSS、JS构建后台涵盖商品分类、上下架、库存调整、订单管理、用户管理等模块前台支持注册登录、商品推荐、购物车、收藏、评论、充值及模拟支付等完整电商流程功能覆盖较全可直接运行或二次改造。资源包共1344个文件压缩后约22.21MB主要包含132个Java后端源码、156个JSP页面、166个CSS与359个JS前端脚本同时提供数据库SQL脚本、配置文件、图片及论文文档前端还集成了Bootstrap、Layui、ElementUI等常用UI库结构清晰便于对照学习。目前已有133人学习下载适合需要毕设参考或商城项目实战练习的读者。1. 为什么说SSM框架是电脑配件销售系统的稳妥选择电脑配件商城这个题目的典型工作量不是把商品表和订单表建出来就完事而是要在规格属性多、价格波动快、下单路径长这三件事里维持数据一致。SSM框架——Spring管理对象与事务、SpringMVC处理请求路由、MyBatis负责SQL映射——恰好从结构上把这三件事拆开Controller管参数和响应Service管业务规则与事务边界Mapper管可控的SQL。相比Spring Boot的全自动装配SSM的每个环节都是显式配置B/S架构下从Tomcat到数据库的调用链更短也更好解释所以数据库课程设计、毕业设计和Java学习路线里仍然大量沿用这套组合。这里给出的做法可以直接落地核心是表结构设计、三层代码拆分、事务边界划分三件事。2. 数据库表设计SSM商城先把商品、库存、分类的边界定清楚2.1 分类表与商品主表为什么不能只建一张product表SSM项目的第一步不是写注解而是把表结构定下来。电脑配件的类目层级比一般商品深CPU下面有Intel和AMD内存下面有DDR4和DDR5显卡还要按显存和功耗再分。如果所有商品都塞进一张product表只靠一个category_id字段区分后期做按分类筛选和导航菜单时SQL会越写越绕。最省事的做法是用parent_id做邻接表CREATE TABLE category ( id INT PRIMARY KEY AUTO_INCREMENT, parent_id INT NOT NULL DEFAULT 0, name VARCHAR(50) NOT NULL, sort_order INT NOT NULL DEFAULT 0, create_time DATETIME DEFAULT CURRENT_TIMESTAMP );parent_id为0时表示顶级分类例如CPU、内存、显卡非0值挂在对应父级下面。导航栏的多级菜单只需要后端提供一个findByParentId方法递归或循环调用都能渲染完查询量不大不需要上存储过程或递归CTE。这样设计的直接好处是后续新增一个水冷散热器分类不需要改表结构只要插入一行记录即可。商品表单独再建不要在category表里塞价格和库存等业务字段CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, category_id INT NOT NULL, name VARCHAR(200) NOT NULL, subtitle VARCHAR(255), price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, sales INT NOT NULL DEFAULT 0, status TINYINT NOT NULL DEFAULT 1, detail TEXT, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_category (category_id), KEY idx_status (status) );这里有几个字段需要特别说明字段用途说明price商品展示价用DECIMAL(10,2)避免FLOAT精度问题stock可售库存下单扣减时的重点保护对象sales累计销量商品列表排序直接使用这个冗余字段status上下架状态商城查询强制加status1条件detail详情HTML内容存富文本编辑器产出的片段减少文件IOstatus字段加了普通索引因为前台商品列表几乎都带status1条件category_id也要建索引列表页关联查询和按分类筛选都靠它。索引不是越多越好但这两个字段在电脑配件商城的高频查询路径上值得加。2.2 SKU表处理器和内存怎么共用一套商品模型规格属性是电脑配件和普通服装最大的差异点。一颗CPU有主频、核心数、插槽类型一根内存有容量、频率和时序如果每个品类都建扩展表Mapper层会迅速膨胀。常见做法是sku表里存spec_json字段把规格参数序列化成JSON字符串展示时解析成键值对CREATE TABLE sku ( id INT PRIMARY KEY AUTO_INCREMENT, product_id INT NOT NULL, spec_json VARCHAR(2000) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT NOT NULL DEFAULT 0, sku_code VARCHAR(50), KEY idx_product (product_id) );sku_code是给渠道对接或仓库盘点用的编码前台不直接展示。product表里保留的price和stock是默认SKU的冗余列表页无需join sku表就能渲染价格区间。当用户进详情页选择具体配置比如内存条选16GB DDR5 5600这套SKU时再去sku表查精确价格和实时库存。这样SSM项目里商品模块只需要ProductMapper、SkuMapper两张基础Mapper就能覆盖所有查询代码量比建品类扩展表少一半。提示spec_json在返回列表时不要一并查出详情页单独select这一列列表页的select不查它能省掉不少网络传输和JSON解析时间。2.3 库存扣减的并发写法update语句直接判断库存字段在product表和sku表各有一份时Service层更新时要注意顺序。下面这个写法是处理并发扣减的标准做法UPDATE product SET stock stock - 1 WHERE id #{productId} AND stock 1;这条SQL把库存判断和扣减放在同一个原子操作里完成。MyBatis执行后返回int如果返回值是0说明库存不足或商品已下架Service层直接抛出业务异常回滚事务。反过来先select查库存再update的写法在B/S架构下多个用户同时下单时会读到相同库存最终造成超卖。真实商城里这个细节决定订单准不准。订单明细里的多个SKU都要扣库存时把这些UPDATE放在同一个方法里用Transactional圈住事务任何一个SKU扣减失败都会整体回滚。需要特别检查的是扣库存的SQL不能包在try-catch里吞掉异常否则Spring感知不到RuntimeException事务不会回滚。3. SSM分层实现Mapper、Service、Controller里怎么把商品列表跑通3.1 用MyBatis的XML写动态查询别依赖逆向工程的通用方法很多SSM项目源码自带MyBatis逆向工程生成的Mapper但基础CRUD只覆盖单表。商品列表需要联category表取分类名、按价格区间筛选、按关键字模糊搜索这些都必须在XML里手写SQL。下面这段是列表页查询常用的Mapper XMLselect idselectPageList resultTypecom.example.shop.vo.ProductVO SELECT p.id, p.name, p.subtitle, p.price, p.stock, p.sales, c.name AS categoryName FROM product p LEFT JOIN category c ON p.category_id c.id where if testkeyword ! null and keyword ! AND p.name LIKE CONCAT(%, #{keyword}, %) /if if testcategoryId ! null AND p.category_id #{categoryId} /if if testminPrice ! null AND p.price gt; #{minPrice} /if if testmaxPrice ! null AND p.price lt; #{maxPrice} /if AND p.status 1 /where ORDER BY choose when testsort salesp.sales DESC/when otherwisep.create_time DESC/otherwise /choose LIMIT #{offset}, #{pageSize} /select这段XML里有三个关键点排查和改参数时最容易在这上面绕标签/参数作用常见误用where自动过滤第一个条件前多余的AND用WHERE 11拼接无法利用索引if参数为空时跳过条件test里只判断null不判断空串choose互斥分支相当于switch写成多个if后面的条件覆盖前面offset/pageSize物理分页参数忘记计算offset导致分页重复取数据where标签比WHERE 11干净配合if把keyword、categoryId、价格区间全部参数化能避免字符串硬拼SQL的注入风险。sort参数只接受白名单内的值比如sales和默认时间排序防止外部把任意表达式带到order by后面。项目初期不需要PageHelper插件直接在SQL里算好offset和pageSize逻辑简单出问题也好定位。3.2 Service层怎么分事务查询不加注解写操作才加SSM的事务管理配置有三处要同时做对在spring配置里声明DataSourceTransactionManager、开启tx:annotation-driven/、Service方法上加Transactional。容易漏的是第三处以及把注解加在了Controller上——SpringMVC的上下文和Spring的上下文是两个容器事务扫描要保证落在Spring容器加载的包名下。事务不生效这个问题经常出现在Java面试题里排查路径就是先看注解位置再看异常有没有被吞。查询方法不需要加事务注解public PageInfoProductVO queryPage(int pageNum, int pageSize, String keyword) { int offset (pageNum - 1) * pageSize; ListProductVO list productMapper.selectPageList(offset, pageSize, keyword); int total productMapper.countPageList(keyword); return new PageInfo(list, total); }而含多次写操作的下单方法必须加Transactional(rollbackFor Exception.class) public Long createOrder(OrderCreateDTO dto) { // 校验用户、收货地址 int rows skuMapper.deductStock(dto.getSkuId(), dto.getQuantity()); if (rows 0) { throw new BizException(库存不足); } // 插入订单主表、插入订单明细表 return orderMapper.insert(...); }rollbackForException.class在这里建议显式写出来因为Spring默认只回滚RuntimeException和Error如果业务异常继承的是普通Exception不回滚会让订单和库存错位。deductStock返回0时抛异常事务会把前面已执行的SQL全部回滚这也是SSM事务最典型的应用场景。3.3 Controller层用ResponseBody返回JSON结构B/S架构下的SSM商城Controller层返回统一JSON比返回JSP视图更适合调试前端拿到数据自己做渲染。同一个Controller方法里不要既写业务逻辑又拼页面下面是最小的商品列表接口Controller RequestMapping(/product) public class ProductController { Autowired private ProductService productService; RequestMapping(/list) ResponseBody public ResultData list(RequestParam(defaultValue 1) int pageNum, RequestParam(defaultValue 16) int pageSize, String keyword) { PageInfoProductVO pageInfo productService.queryPage(pageNum, pageSize, keyword); return ResultData.success(pageInfo); } }参数说明pageNum和pageSize都设默认值前端漏传参数时后端不会空指针keyword允许为空Mapper里的动态SQL会自己跳过模糊条件。ResultData是统一响应体包含code、msg、data三个字段前端根据code判断请求是否成功。如果项目里还想用JSP渲染列表去掉ResponseBody改返回ModelAndView即可两种形态在SSM里切换成本很低这也是这套框架做B/S架构时比较灵活的地方。4. 购物车与订单SSM项目的事务边界怎么划才不出超卖4.1 购物车存SESSION还是存数据库表购物车实现有两条路存Session代码简单、未登录也能加购但换设备就丢存数据库表需要建cart和cart_item表数据持久但每次请求都查库。SSM商城里比较稳妥的是混合方案未登录时购物车放Session登录后把Session里的数据合并进数据库。CREATE TABLE cart_item ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, sku_id INT NOT NULL, quantity INT NOT NULL DEFAULT 1, selected TINYINT NOT NULL DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_user_sku (user_id, sku_id) );购物车表设计的重点在UNIQUE KEY上。同一个用户往购物车加同一个SKU第二次时应该执行ON DUPLICATE KEY UPDATE把数量加一而不是再插入一条重复记录。登录合并Session购物车时也依赖这个唯一键做合并避免出现同一个人购物车里两条相同配件的尴尬。selected字段标记用户是否勾选该商品生成订单时只处理selected1的行。4.2 下单时的事务顺序库存、订单主表、订单明细怎么保持一致下单操作涉及多次写库事务边界要圈住整个方法。正常的执行顺序是查SKU价格、扣库存、写订单主表、写订单明细、清空购物车。前两步和后三步如果分开提交就会出现在库存扣了但订单没有生成的情况对不上账。推荐在Service层做同一批操作Transactional(rollbackFor Exception.class) public Long createOrder(OrderCreateDTO dto) { // 1. 查询购物车中勾选的条目 // 2. 遍历每个sku执行扣减库存累计总价 // 3. 插入订单主表得到orderId // 4. 批量插入订单明细 // 5. 删除购物车中对应条目 // 6. 返回orderId }注意订单主表最好预留id和OrderNo两个字段。id用自增方便关联明细OrderNo是业务订单号生成规则建议用时间戳加用户ID后四位加随机数对外展示不要暴露自增ID避免别人通过遍历ID抓取全部订单。做批量插入明细时MyBatis的foreach标签配合insert可以一次提交完成但注意batch大小控制在500以内超过这个量会出现参数过长。4.3 订单状态流转改状态用条件update而不是先查后改订单状态推荐用tinyint字段存数字枚举而不是直接存中文。原因是中文状态一旦改名就要全表UPDATE数字配合常量类维护变更成本更低。常见状态定义如下状态值含义可流转到0待支付1、41已支付待发货2、42已发货33已完成无4已取消无状态变更时要防止重复提交和并发修改比如支付回调把订单从0改成1如果回调重试第二次请求可能把已发货的订单改回待支付。这里必须要条件更新UPDATE orders SET status 1, pay_time NOW() WHERE id #{orderId} AND status 0;返回值是0说明状态已被改过直接忽略重复回调。SSM项目的订单状态机里每一组状态变更都应该写成这种条件UPDATE而不是先查后改。这个写法看似简单却是商城项目少出bug的关键所有涉及状态跃迁的地方都统一遵守这套规则。5. B/S架构部署验证与四个容易踩的坑5.1 用两条SQL验证库存和订单一致性项目部署到Tomcat后不要只点几个页面就完事。我一般会模拟一个真实下单流程然后跑两条SQL确认没超卖、没丢单。第一条验证库存没有扣成负数SELECT COUNT(*) FROM product WHERE stock 0;第二条验证订单明细和订单主表没有孤儿数据SELECT od.order_id, o.id FROM order_item od LEFT JOIN orders o ON od.order_id o.id WHERE o.id IS NULL;两条查询都返回0说明事务回滚和删除逻辑基本正常。还可以再跑一条统计SQL验证扣减库存总数等于订单明细数量之和这条能发现重复扣库存和漏扣库存两个方向的问题在MySQL客户端或Navicat里直接执行即可。5.2 部署时最容易踩的四个坑第一个坑是Tomcat版本和JDK版本不匹配。SSM老项目源码用Java 8编译时部署到Tomcat 10会因Jakarta命名空间报ClassNotFoundException建议用Tomcat 9配JDK 8省去改包名的工作量。第二个坑是数据库连接串没加时区参数。MySQL 8的JDBC驱动必须带serverTimezoneAsia/Shanghai否则日期字段全部错乱报错信息只会提示连接失败不会直接指出时区问题。第三个坑是SpringMVC拦截器拦截了/css、/js、/images路径页面样式全部丢失。需要在spring-mvc.xml里配置放行规则或者让拦截器路径避开静态资源目录。第四个坑是jdbc.properties里数据库密码明文。毕业设计可以直接这样写但真实商城项目建议用Jasypt对连接串里的密码做加密配置里只放密文。这个改动量很小但对技术答辩有实际加分。排错时优先看MyBatis打印的SQL和参数。把mapper包对应类的日志级别调到DEBUG每次执行前会打印Preparing和Parameters两行哪怕业务逻辑复杂也能快速定位到是SQL写错还是参数传错。本文还有配套的精品资源点击获取
返回列表