资讯动态

SSM商城项目实战:Spring+SpringMVC+MyBatis整合与部署指南

发布时间:2026/9/11 18:53:13 来源:尧图企业网站定制
简介这是一套基于SSM框架实现的电脑配件销售商城系统专为毕业设计、Java Web课程设计与入门实践打造覆盖前台商城与后台管理两大业务场景。系统角色包括管理员与用户后台提供商品分类、商品上下架、库存增减、订单管理、新闻资讯、轮播图配置等功能前台支持注册登录、商品浏览推荐、收藏、购物车、立即购买、发表评论、地址管理、余额与积分充值及模拟支付等完整交易闭环。技术采用JDK1.8、MySQL5.7结合SpringSpringMVCMyBatisMaven搭建前端基于JSP、CSS、JavaScript实现IDE使用Eclipse或IDEA均可运行。资源压缩包共1344个文件约22.21MB以359个js脚本、172个png图片、166个css样式、156个jsp页面、132个java源码及2个sql数据库脚本等构成源码目录清晰便于按模块查阅与二次开发。当前已有133人学习适合需要参考完整商城项目、快速搭建SSM框架应用并理解前后台交互逻辑的开发者。1. 从一套可运行的SSM商城说起接手毕业设计或课程设计时最怕的是拿到手跑不起来。这套基于SSM框架的电脑配件销售商城jdk1.8 MySQL5.7 Tomcat8IDEA或eclipse直接导入Maven工程就能启动。它不是那种只有空壳页面的演示项目前台有注册登录、商品推荐、购物车、下单支付、评论收藏后台有用户管理、商品上下架、库存增减、订单处理和轮播图设置。对于正在做Java技术栈毕业设计的人或者想靠“能跑起来的SSM项目”来验证自己是否真正理解SpringSpringMVCMyBatis整合的人都值得拆开研究一遍。下面我按技术选型、数据库、核心流程、部署验证这条线索把可复现的部分和几个容易踩的坑一起讲清楚。2. SSM框架的职责划分与B/S架构落地2.1 为什么选SSM而不是Spring Boot现在很多新项目都直接用Spring Boot但SSM作为Java Web的经典组合在面试八股文里出现频率非常高。Spring负责管理Bean依赖SpringMVC处理浏览器发来的HTTP请求MyBatis把SQL和Java方法做映射。三者各管一段边界非常清楚。这个项目采用B/S架构浏览器作为客户端请求先到TomcatSpringMVC的DispatcherServlet根据URL找到ControllerController调用ServiceService通过Mapper操作MySQL最后将结果渲染到JSP页面。从学习角度看用SSM手动拼接配置xml能让你看清组件之间的依赖关系而不是被Spring Boot的自动配置遮住。从毕设答辩角度讲这套框架既能说明白原理也能展示你对Maven分模块、事务控制、SQL优化的理解。我拿这个项目做模板时会格外注意观察它的配置文件是否真的把扫描路径和事务切面分开因为很多网上的SSM项目在这两块是乱的。2.2 Maven依赖与三层结构初始化项目使用Maven管理依赖引入spring-mvc、mybatis-spring、mysql-connector-java等。先看pom.xml的核心依赖这样导入IDE后不会因为包冲突跑不起来。properties spring.version5.1.8.RELEASE/spring.version mybatis.version3.5.2/mybatis.version /properties dependencies !-- Spring MVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version${spring.version}/version /dependency !-- MyBatis 整合 -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.3/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version${mybatis.version}/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.47/version /dependency !-- JSP相关 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency /dependenciesSpring版本和MyBatis版本要配套我遇到过把mybatis-spring升到2.0.3而Spring还在4.x导致SqlSessionFactory初始化报错的情况。这里Spring5配MyBatis3.5是稳定组合。另外注意mysql驱动包版本如果数据库是MySQL8驱动类名和url需要调整。2.3 核心配置spring-mvc.xml与applicationContext.xmlSSM项目里有两类配置SpringMVC管web层Spring管service和dao。先看spring-mvc.xml它负责开启注解驱动、扫描Controller、配置视图解析器。context:component-scan base-packagecom.pcshop.controller/ mvc:annotation-driven/ bean classorg.springframework.web.servlet.view.InternalResourceViewResolver property nameprefix value/jsp// property namesuffix value.jsp/ /beanbase-package只扫描controller包不然会把Service也扫进web上下文造成事务配置失效。InternalResourceViewResolver把Controller返回的字符串拼成/jsp/xxx.jsp路径。也就是说JSP页面统一放在webapp/jsp目录下这是约定不要乱改否则会404。再来看applicationContext.xml它管理数据源、SqlSessionFactory、事务管理器、Mapper扫描。bean iddataSource classorg.apache.commons.dbcp.BasicDataSource property namedriverClassName valuecom.mysql.jdbc.Driver/ property nameurl valuejdbc:mysql://localhost:3306/pc_shop?useSSLfalse/ property nameusername valueroot/ property namepassword value123456/ /bean bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ property namemapperLocations valueclasspath:mapper/*.xml/ /bean bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.pcshop.dao/ /bean bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean数据源用DBCP连接池url中useSSLfalse是MySQL5.7常见的参数避免本地开发时因为SSL握手报warning。SqlSessionFactory的mapperLocations指向resources/mapper下的XML文件这意味着SQL语句独立写在XML里而不是注解里。如果你拿到的源码里没有mapper目录多半是没把resources目录标记为源码根目录启动时会一直报Invalid bound statement (not found)。2.4 数据模型与角色权限表该系统角色是管理员和用户对应后台、前台两套界面。从数据库设计的角度看主要表包括用户表、商品分类表、商品信息表、新闻资讯表、轮播图表、订单表、订单项表、收藏表、地址表、余额/积分账户表。下面列出核心表的作用与关系。数据表核心字段作用userid, username, password, balance, points前台用户登录、余额/积分充值categoryid, name, sort商品分类比如CPU、显卡、内存productid, name, price, stock, status, recommend商品信息上下架用status区分ordersid, user_id, total_price, status, address订单主表记录支付和发货状态order_itemid, order_id, product_id, quantity订单明细记录每个商品购买数量favoriteid, user_id, product_id用户收藏与商品多对多关系addressid, user_id, detail, phone收货地址结算时使用这套表的职责划分是典型的B/S电商结构。status字段在商品表里先定义为tinyint1表示上架0表示下架前端“商品上架/下架功能”就是更新这个字段。订单表里也会有一个状态机从待付款、待发货、待收货到已完成后台订单管理实际就是对这些状态的流转维护。理解表结构后再去看MyBatis的Mapper映射才会有“它在查哪张表、改哪个字段”的实感。3. 数据库设计与MyBatis映射从ER图到商品库存扣减3.1 建表SQL与ER图要点拿到源码后先把数据库脚本导入MySQL5.7。脚本里会包含建库、建表、插入初始数据。如果你是新做的数据库课程设计ER图需要手画但表的字段可以直接参考这个项目。商品表和订单表是根其他表都围绕它们展开。CREATE TABLE product ( id int(11) NOT NULL AUTO_INCREMENT, category_id int(11) DEFAULT NULL, name varchar(128) NOT NULL, price decimal(10,2) NOT NULL, stock int(11) NOT NULL DEFAULT 0, status tinyint(1) DEFAULT 1, recommend tinyint(1) DEFAULT 0, image varchar(255) DEFAULT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE orders ( id int(11) NOT NULL AUTO_INCREMENT, user_id int(11) NOT NULL, total_price decimal(10,2) NOT NULL, status tinyint(2) NOT NULL DEFAULT 0, receiver_name varchar(32) DEFAULT NULL, receiver_phone varchar(20) DEFAULT NULL, receiver_address varchar(255) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8; CREATE TABLE order_item ( id int(11) NOT NULL AUTO_INCREMENT, order_id int(11) NOT NULL, product_id int(11) NOT NULL, quantity int(11) NOT NULL, sub_total decimal(10,2) NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8;price用decimal(10,2)不要用float否则运算会出现精度问题。订单总金额在orders表冗余存储这样查询订单列表时不用每个订单都去做一次聚合计算。order_item表记录商品快照式的价格和数量就算之后商品改价历史订单也能保持原金额。所有表都指定ENGINEInnoDB保证事务和行级锁生效这是后面库存扣减不超卖的基础。3.2 MyBatis动态SQL与增删改查项目里的SQL写在mapper XML中。以商品信息管理为例列表页需要根据分类、关键字、上下架状态做条件查询MyBatis的动态SQL刚好处理这种场景。看下面这段商品查询和库存更新的写法。select idselectProductList resultTypeproduct SELECT id, name, price, stock, status, recommend FROM product where if testcategoryId ! null AND category_id #{categoryId} /if if testkeyword ! null and keyword ! AND name LIKE CONCAT(%, #{keyword}, %) /if if teststatus ! null AND status #{status} /if /where ORDER BY id DESC /select update idupdateStock UPDATE product SET stock stock #{delta} WHERE id #{productId} /update标签会自动去掉多余的AND不用拼接字符串时担心多一个and。LIKE查询用CONCAT拼接参数避免sql注入风险。updateStock这里故意写成“stock stock delta”而不是传出新的stock再覆盖目的是支持上架增加库存、下架减少库存的通用操作。delta为负数就是减少库存。在我拿到的这套源码里“添加或减少库存功能”调用的就是这类SQL而不是直接把库存改成固定值。3.3 库存扣减的原子性与事务边界下单时“立即购买”和“加入购物车”都能进入结算但真正扣减库存的时机是在订单生成时。如果两个用户同时买最后一件商品数据库层面做不到原子扣减就会超卖。我在这个项目里验证过只加Transactional还不够还得用正确的SQL。对比三种写法方式SQL/操作是否会超卖查库存-判断-更新select stock; if(stock0) update set stockstock-1是并发下读旧值乐观锁版本号update set stockstock-1, versionversion1 where version#{old};否但需重试悲观锁行锁select stock from product where id#{id} for update否但会锁行这个项目事务边界覆盖“扣库存生成订单清购物车”我建议在Service层方法上使用Transactional并且把库存更新语句放在订单插入之后。常见误用是只在DAO或Controller上写注解那样事务会提前提交。另外注意隔离级别默认是REPEATABLE_READMySQL默认如果订单状态需要在并发下立刻可见可以显式设置成READ_COMMITTED。对毕设而言理解到“update必须带条件且依赖数据库行锁”这一层就足够答辩了。4. 用户购物车、下单与支付流程的完整实现4.1 前端JSP与Controller的请求映射前台功能包括用户注册登录、首页商品推荐、商品详情、点我收藏、加购物车、立即购买、评论等。JSP放在/jsp目录通过Controller暴露URL。以商品详情和加入购物车为例映射关系如下。前端操作HTTP请求Controller方法点击商品详情GET /product/detail?id10ProductController.detail加入购物车POST /cart/addCartController.add提交订单POST /order/createOrderController.create收藏商品POST /favorite/addFavoriteController.add在CartController里核心逻辑是从Session取出当前用户再根据productId把商品加入购物车集合。如果购物车已存在同款商品数量加1否则新增一行。因为购物车只是临时数据并发并不高用内存Map保存即可订单生成后再落库到order_item。4.2 下单与支付Service层的事务编排OrderController.create接收前端提交的地址和商品列表然后调用OrderService。我在本地改造时把整个下单流程写成了这样Transactional(rollbackFor Exception.class) public Integer createOrder(OrderVO orderVO, Integer userId) { // 1. 根据商品ID集合查出最新价格和库存 ListProduct products productDao.selectByIds(orderVO.getProductIds()); // 2. 计算总价同时校验库存 BigDecimal totalPrice BigDecimal.ZERO; for (CartItem item : orderVO.getCartItems()) { Product p findProduct(products, item.getProductId()); if (p.getStock() item.getQuantity()) { throw new BizException(库存不足); } totalPrice totalPrice.add(p.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))); } // 3. 创建订单主记录 Order order new Order(); order.setUserId(userId); order.setTotalPrice(totalPrice); order.setStatus(0); // 待支付 orderDao.insert(order); // 4. 扣减库存注意这里是关键 for (CartItem item : orderVO.getCartItems()) { productDao.updateStock(item.getProductId(), -item.getQuantity()); } return order.getId(); }createOrder方法上的Transactional确保check库存、insert订单、update库存这三个步骤在同一个事务里。rollbackFor Exception.class是必须的Spring默认只回滚RuntimeException如果你抛出的是BizException继承自Exception不写rollbackFor的话事务不会回滚库存照样扣了但订单没建。这里的参数说明orderVO是前端提交的VO对象包含cartItems和addressIdcartItems里只需要productId和quantity价格以数据库实时价格为准不能信任前端传来的单价。第4步使用前面那个updateStock利用行锁保证并发安全。这里我一般还会在insert订单前加一段“余额是否足够”的校验把totalPrice和user.balance做比较避免用户直接绕过前端。4.3 模拟支付与余额/积分充值项目没有对接支付宝/微信支付操作是在后台模拟完成。用户点“支付”后后台把订单状态从0改成1同时扣减用户余额增加对应的积分。下面这段代码来自我简化后的PayService逻辑非常直白public void pay(Integer orderId, Integer userId) { Order order orderDao.selectById(orderId); if (order null || order.getStatus() ! 0) { throw new BizException(订单状态非法); } int rows userDao.deductBalance(userId, order.getTotalPrice()); if (rows 0) { throw new BizException(余额不足请先充值); } orderDao.updateStatus(orderId, 1); userDao.addPoints(userId, order.getTotalPrice().intValue()); }deductBalance就是执行update user set balance balance - #{amount} where id #{userId} and balance #{amount}返回0表示没有行被更新也就是余额不足。这一段结合前面的库存扣减其实是同一个思想把判断条件放到update的where语句中利用数据库行锁保证原子性。这种写法比先查询再判断安全得多。购物车结算与立即购买的区别在于立即购买直接跳转到确认订单页购物车结算会把选中的多个购物车项一起转成订单明细。两类操作最终都会进入相同的createOrder流程只是一开始时商品来源不同。我在调试时遇到过一个问题结算后购物车没有清空因为前端跳转后没有调用移除接口。后端的做法是下单成功后在Service里同时调用cartDao.clearByUserId(userId)这句要放在updateStatus之后确保事务提交后购物车数据一致。4.4 评论、收藏与站长推荐商品详情页的“点我收藏”会向favorite表插入一条记录重复收藏时用唯一索引user_idproduct_id防重设计表时就要加唯一键。评论功能则比较简单评论表记录用户id、商品id、内容、时间列表按时间倒序。至于“站长推荐”我看了源码它其实是在product表上加了recommend字段后台勾选推荐商品时置为1前台首页根据recommend1查询并展示。如果你想让推荐位真正生效要注意首页查询语句里有没有把status1上架作为过滤条件否则下架商品也会出现在推荐位。5. 后台管理功能与部署验证商品上下架、轮播图与订单处理5.1 后台功能清单与权限控制后台功能包括登录、首页统计、个人中心、修改密码、用户管理、商品分类管理、商品信息管理、商品上下架、库存增减、新闻资讯、轮播图管理、订单管理。在代码里后台所有URL都通过拦截器校验session中的admin对象是否存在。前台用户即使猜到后台地址没有管理员session也会被重定向到登录页。这个拦截器我在优化时建议增加一个简单的角色字段判断而不是只判断是否登录。商品上架和下架本质上就是更新product表的status字段后台管理页提供按钮切换。库存增减可以直接用第3章那个updateStock增加时传入正整数减少时传入负整数代码里加减号不要写反。轮播图管理则是上传图片路径存到banner表前台首页加载路由图片。5.2 部署到Tomcat并验证核心链路将源码以Maven项目方式导入IDEA后先编译再配置Tomcat8。注意项目的JDK版本选择1.8语言级别也设为8。部署到Tomcat的步骤如下。mvn clean package -DskipTests打包后target目录下会生成war包把war包放到Tomcat的webapps目录启动Tomcat后会自动解压。或者在IDEA里点击Run选择Tomcat ServerDeployment中选中war exploded。启动后访问http://localhost:8080/电脑配件商城/ 或具体context-path应当能看到首页。验证核心链路我一般按这个顺序走注册一个用户登录将商品加入购物车去结算模拟支付。然后在MySQL中执行一条查询确认订单表和订单明细表里出现了数据且product表对应商品库存减少。这一步如果SQL查询结果符合预期就说明从Controller到Service到Mapper的整条链路是通的。5.3 常见启动异常与解决思路我在跑这类SSM项目时遇到过两类高频问题。第一类是启动时报ClassNotFoundException比如javax.servlet.jsp.jstl.core.Config通常是缺少JSTL依赖在pom.xml引入jstl 1.2即可。第二类是访问数据库报Access denied for user rootlocalhost原因是applicationContext.xml里的数据库密码和本地MySQL不一致需要同步修改。还有一类是页面上JSP取不到值显示null或0多半是Controller返回的ModelAndView里key名与JSP中EL表达式不一致比如JSP写${product.name}而Controller放的是${goods}这需要对比前后端参数名。如果遇到Invalid bound statement (not found)检查applicationContext.xml中mapperLocations配置的路径与resources下xml文件位置是否真的匹配。配置用的是classpath:mapper/*.xml那xml文件必须放在src/main/resources/mapper目录下否则DAO接口和SQL映射无法绑定。这几点排查下来大部分SSM项目都能恢复运行。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价