ARTICLE DETAIL

资讯详情

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

SSM+JSP+MySQL网上商城系统:从原理到实战的JavaWeb项目解析

SSM+JSP+MySQL网上商城系统:从原理到实战的JavaWeb项目解析 先说我最初为什么想写这套“基于javaweb和mysql的ssm网上商城系统”。这两年经常有读者来问同一个问题SSM这套东西是不是已经过时了为什么课程设计、毕业设计还老让做这种项目网上现在铺天盖地都是Spring BootVue的教程一眼看过去好像不会点前后端分离都不好意思说自己是搞Java的。可真去翻招聘JD、去翻企业里的存量项目就会发现JavaWeb生态里SSMJSPMySQL这套组合依然占着很大一块地盘——高校实验课、企业内部老系统、各类“JavaWeb项目完整案例”都还在用它。更重要的是这套系统像一座桥它把Servlet、Spring IOC/AOP、Spring MVC、MyBatis、JSP这些Java后端最基础的东西串在了一起。你可以用Spring Boot三秒钟启动一个项目但你不一定能说清楚DispatcherServlet到底把请求交给了谁、MyBatis的Mapper代理是怎么被注入到Controller里的SSM会逼着你把这件事弄明白。这套商城系统说白了就是一套标准的前后端不分离的Web应用Java作为后端语言SSMSpring Spring MVC MyBatis作为主体框架JSP在服务端渲染页面MySQL存业务数据。它能做的事很具体——用户注册登录、商品分类浏览、购物车管理、下单结算、后台商品管理一个小商城该有的主干流程全都有。适合谁一类是准备JavaWeb课程设计或毕业设计的同学另一类是刚学完Servlet和JDBC、想看看一个完整项目怎么把这些知识组织起来的新手。你照着源码跑通一遍再跟着我文章里的思路自己改一两个功能整个JavaWeb的知识结构会比刷十遍视频都清晰。1. 这个商城系统的定位为什么SSMJSP至今仍然值得细品1.1 不是所有商城都需要微服务一听到“商城系统”很多人第一反应是分布式、微服务、Redis秒杀。但实际上一个小型网上商城最核心的诉求是什么把商品展示给用户让用户下单让管理员维护数据。它的访问量在课程设计、毕业设计、企业内部小规模业务场景下根本触发不了“高并发”这个话题。SSMJSP这套组合在这个场景下有一个特别实际的优点每个环节都能断点调试。出了问题你可以顺着jsp页面 → Controller → Service → Mapper一条链路跟下来不像前后端分离项目查个bug要同时开前端DevTools和后端日志两边来回对照。我带过好几个做毕业设计的同学最后都是靠这套思路把问题定位到具体一行代码的。1.2 SSM背后的知识体系是实打实的面试考点坦白讲Spring Boot并没有消灭SSM它只是把SSM时代的很多配置自动化了。你要是没搞懂Spring容器是怎么启动的、DispatcherServlet在web.xml里扮演什么角色、MyBatis的SqlSessionFactory是怎么和Spring整合的那用起Spring Boot来遇到配置类失效、自动装配不生效这类问题会更加一头雾水。很多Java面试题看着花里胡哨翻来覆去考的还是IOC、AOP、Bean生命周期、事务传播行为这些底子。而这些底子恰恰是做SSM商城项目的时候一个坑一个坑踩出来的。所以别把这个项目当作“过时的老古董”把它当成一个“能让你看懂Spring Boot背后原理”的训练场。1.3 系统的功能边界与整体架构从功能上说这套SSM网上商城系统一般分前台和后台两个部分模块功能点核心表前台-用户注册、登录、个人信息维护user前台-商品分类浏览、关键词搜索、分页列表、商品详情category, product前台-交易购物车增删改、下单、订单列表、订单状态管理cart, orders, order_item后台-管理管理员登录、分类管理、商品上下架/库存修改、订单处理admin_user 等技术架构上就是标准的SSM三层Spring容器管Service层和DAO层Spring MVC管表现层MyBatis管SQL映射。这套代码跑起来后你拿一张流程图去对照会发现每一个请求的走向都是清晰可追的 —— 这也是它作为教学项目的最大价值。2. 数据库设计商城系统表结构从头理清2.1 核心表的划分逻辑一个能跑通所有业务的商城表不需要太多但每张表都得有它存在的理由。我见过不少学生项目表建得乱七八糟订单里直接存商品快照的、购物车和订单混在一张表的后期写SQL的时候叫苦连天。我建议按“用户维度 商品维度 交易维度”来拆这是最不容易出错的方式。user表主要存账号密码、昵称、联系方式product表存商品名称、价格、库存、描述、图片路径并外键关联分类IDcategory表存商品分类实现商城顶部那个“手机 / 电脑 / 配件”的导航orders表和order_item表是典型的“主表-子表”结构一次下单操作主表插入一条订单记录子表插入多条商品明细。为什么要拆订单主表和明细表因为订单有整体状态待付款、待发货、已完成、已取消而明细要单独记录每个商品的购买数量和当时的成交价格这样后期改订单里的某一个商品才不会把整条订单记录搞乱。2.2 商品与分类的级联关系商品表里通常会存一个 category_id关联到分类表的主键。这里有两点要注意第一分类删除时怎么办如果直接把分类删了商品表的外键约束会直接报错如果不加外键又会出现“商品挂在了一个不存在的分类下”这种脏数据。实操中最稳妥的处理是删除分类前先检查该分类下是否还有商品如果有就不能删前台页面提示“该分类下存在商品无法删除”。这个逻辑写在Service层用MyBatis查一下商品表里 category_id ? 的记录数比在数据库层面直接挂级联删除要安全得多。第二商品图片的存储。别把图片转成base64塞进数据库那是给新手演示用的生产环境这么干数据库会迅速膨胀。我建议在product表里只存图片的URL路径例如 /upload/123.jpg图片文件本身放到本地磁盘或单独的静态资源目录项目里通过Tomcat的虚拟路径映射去访问。这样既能让图片在JSP页面里正常访问又避免数据库被大字段拖垮。2.3 订单与购物车的状态机设计购物车表有两种常见设计一种是把购物车数据存在Session里用户不登录也能加购物车另一种是建一张cart表每条记录对应某个用户的某个商品及其数量。这套系统里如果用了MySQL存储购物车那表结构大概是cart_id, user_id, product_id, quantity, created_time联合唯一索引user_id, product_id保证同一个用户同一个商品只有一条记录重复添加时执行的是数量累加而不是插两条。订单表的状态字段我强烈建议用一个int类型的status来管理枚举值而不是存一段中文字符串。0代表待付款、1代表待发货、2代表已完成、3代表已取消这样代码里写判断条件很清爽后续做订单状态流转的统计报表也方便。订单编号不要用MySQL自增主键直接展示给用户很容易被猜到订单量建议用时间戳拼接随机数生成一个业务订单号比如yyyyMMddHHmmss 4位随机数。2.4 MySQL连接与字符集配置版本8尤其注意如果你用的是MySQL 8连接URL里那串参数一定要写对这是SSM项目跑起来第一个最容易卡住的地方jdbc:mysql://localhost:3306/mall?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueuseSSLfalse是因为本地开发环境通常没有配置SSL证书MySQL 8默认又把SSL开关打开了不关掉会报“SSL connection error”。serverTimezoneAsia/Shanghai是因为MySQL 8驱动要求明确指定时区不然会因为服务器时区和系统时区不一致报错。这个坑在热搜里也占了很大比例说明大家都在同一个地方摔倒过。建表时统一用utf8mb4而不是utf8原因很简单utf8在MySQL里最多只能存3字节的字符4字节的emoji表情和生僻字会存不进去商城评价、用户昵称里出现emoji是大概率事件所以设计阶段一次性用对省得后面整个库去改字符集。3. 项目骨架搭建Maven SSM 的经典三步走3.1 Maven项目结构初始化这套系统用Maven聚合工程来管理是最省心的。但如果你是课程设计没必要搞那么复杂一个war包的单模块工程就够了。在IDEA里新建Maven项目勾选“Web Application”模板得到的目录大概是这个样子src/main/java 放包结构com.example.mall src/main/resources 放Spring、MyBatis等XML配置 src/main/webapp 放JSP、CSS、JS、静态资源 pom.xml 依赖管理pom.xml里最核心的依赖是spring-webmvc、spring-jdbc或者spring-tx用于事务、mybatis、mybatis-spring、mysql-connector-java、javax.servlet-api、jstl。如果是Maven编译时提示找不到servlet-api注意加provided作用域因为Tomcat里本来就有servlet的jar包打包时不需要带进去不然会和Tomcat自带的冲突报错。3.2 web.xml与Spring容器初始化SSM项目里web.xml是整条链路的起点。两个关键配置必须在里面ContextLoaderListener监听器用来初始化Spring的根容器负责装配Service和Mapper那部分Bean。DispatcherServletSpring MVC的前端控制器所有以.do或/结尾的请求都会先到它手里再由它分配去找对应的Controller。新手最容易漏的是Spring容器的配置文件applicationContext.xml只管DAO、Service、事务、数据源Spring MVC的配置文件springmvc.xml只管Controller、视图解析器、静态资源放行。两者职责不同如果都塞到同一个配置里虽然小项目也能跑但后期加了事务、加了拦截器各种莫名其妙的问题就会冒出来。3.3 Spring MVC 与 MyBatis 配置要点springmvc.xml里最有存在感的是三样组件扫描、注解驱动、视图解析器。组件扫描要精准只扫com.example.mall.controller包注解驱动直接写mvc:annotation-driven/它会把HandlerMapping、HandlerAdapter这些默认组件注册好视图解析器配置上InternalResourceViewResolverJSP文件放在/WEB-INF/pages/目录下前缀是/WEB-INF/pages/后缀是.jsp。这里有个讲究JSP放在WEB-INF目录下用户没法直接在浏览器地址栏输入URL访问只能通过Controller转发进去。对于商城这种包含后台管理页面的系统这一层“地址不可直接访问”的保护很重要不然用户猜一下URL就能看到后台管理界面了。MyBatis的配置主要分三块mybatis-config.xml管全局配置比如开启驼峰映射mapUnderscoreToCamelCasetruemapper.xml文件写SQLspring集成配置里用SqlSessionFactoryBean把数据源和mapper.xml关联起来再用MapperScannerConfigurer扫描DAO接口生成代理对象。这样你在Service里注入Mapper接口时Spring就会把代理Bean塞给你你只管调方法SQL执行细节全给MyBatis了。3.4 静态资源池与JSP页面的关系只要你在web.xml里把DispatcherServlet映射成/它就会拦截所有请求包括CSS、JS、图片。如果你不做处理就会发现页面打开后长得跟裸奔一样。解决方案在springmvc.xml里加mvc:resources location/static/ mapping/static/**/把静态资源统一放到webapp/static目录下JSP页面里引用路径写成${pageContext.request.contextPath}/static/css/style.css。这样请求/static下的资源不会被DispatcherServlet接管而是直接由Tomcat默认servlet处理。这个细节如果你是第一次整合SSM十有八九会踩而且报错还不直观——页面找不到CSS通常不会报后台异常只是样式全丢。4. 核心业务实现登录鉴权、商品浏览与购物车4.1 登录拦截器与会话管理商城系统里“购物车、下单、个人中心”这些操作必须登录后才能执行。实现方式有很多最标准和适合SSM的是写一个HandlerInterceptor拦截器在preHandle方法里检查Session里有没有登录用户public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object user session.getAttribute(user); if (user null) { response.sendRedirect(request.getContextPath() /login); return false; } return true; }然后在springmvc.xml里注册这个拦截器记得用mvc:exclude-mapping把登录接口、注册接口、商品列表、商品详情这几个不需要登录的路径放行不然用户连商品都没看到就被踹去登录了这个体验很糟糕。密码这块千万不能明文存进数据库。即使这是一个教学项目我也建议至少用MD5加盐或者BCrypt做一次哈希。实操里MD5加盐比较简单用户注册时取随机盐 密码做MD5盐和哈希值一起存库登录时用同样的盐再算一次比对。虽然MD5现在不算绝对安全但作为课程设计级别的项目已经能体现出“密码不能明文存储”这个基本安全意识了。你把这招写在设计说明书里答辩老师是很吃这一套的。4.2 商品列表的分页与多条件筛选商品列表页是商城前台访问量最大的页面它要同时支持分类筛选、关键词搜索和分页。分页这块如果你不想引入PageHelper插件直接手写也是锻炼基本功的好机会。核心思路是前端传page当前页和pageSize每页条数MyBatis的查询SQL里加上LIMIT #{offset}, #{pageSize}offset计算成(page - 1) * pageSize。同时查询满足条件的总记录数算出总页数。多条件查询的SQL写法有个小技巧用MyBatis动态SQL来拼接select idsearchProducts resultTypecom.example.mall.entity.Product select * from product where if testkeyword ! null and keyword ! and name like concat(%, #{keyword}, %) /if if testcategoryId ! null and categoryId ! 0 and category_id #{categoryId} /if /where order by id desc limit #{offset}, #{pageSize} /selectwhere标签会自动处理掉多余的and这是MyBatis最实用的一个用法。如果你用PageHelper记得在查询前调用PageHelper.startPage(page, pageSize)且它只作用于接下来的第一条查询多个查询混在一起时很容易分页数据错乱。4.3 购物车设计存Session还是存MySQL这两个方案在这个项目里都存在。存Session的好处是用户不登录也能加购体验好实现也简单——购物车Map挂在Session里增删改查都走内存。坏处是用户换电脑、清浏览器购物车就消失了而且订单结算时如果用户中间登录还得处理Session里的临时购物车怎么合并到数据库里。存MySQL的好处是数据持久化后台能看到每个用户的购物车明细。坏处是每次操作都要连一次数据库性能略差而且匿名用户没法操作。我的建议是课程设计/毕业设计直接存MySQL理由有两点。第一答辩的时候老师问“购物车是怎么存的”你说“用了MySQL表存和订单表关联”比“存在Session里”显得更完整第二存MySQL才好练到Service事务那部分——结算时把购物车数据取出来生成订单然后清空购物车这是一个非常典型的事务场景比单纯背事务概念有用多了。4.4 订单创建与库存扣减事务必须加上下单是整个系统里最需要事务保护的逻辑。简单说一次下单要干三件事往orders表插一条订单主记录往order_item表插这个订单的商品明细把product表里对应商品的库存减掉把该用户的购物车记录清空。这四个操作任何一步失败都不能让其他的成功否则会出现“订单建了但商品明细没了”或者“库存减了但订单没生成”这种脏数据。实现方式非常简单在Service方法上标注Transactional(rollbackFor Exception.class) public boolean submitOrder(Order order) { // 插入订单 // 插入明细 // 扣库存 // 清购物车 return true; }rollbackFor Exception.class这个参数值得说一下默认情况下Spring事务只在遇到RuntimeException时回滚如果代码里catch了异常后吞掉事务就失效了。你明确指定所有Exception都回滚就避免了检查型异常导致的数据不一致问题。扣库存的SQL记得加上库存判断条件update product set stock stock - #{quantity} where id #{productId} and stock #{quantity}受影响行数为0就说明库存不足直接抛异常回滚防止出现负库存。5. 一路上最容易踩的坑从404到500的排查实录5.1 请求404先查映射再查web.xmlSSM项目里遇到404九成和三个地方有关RequestMapping写错路径、Controller没有被扫描到、web.xml里DispatcherServlet的url-pattern配错。我排查这类问题有一个固定顺序先看Tomcat启动日志有没有报“Mapping”相关的异常再看浏览器请求的URL和Controller里的注解是否严格一致大小写、斜杠都不能错最后打开SpringMVC的debug日志看有没有一行“Mapped to public String xxxController.xxx()”。你如果发现一个能访问一个不能访问多半是路径问题如果全部都404优先怀疑组件扫描没扫到Controller包。5.2 mysql连接报SSL错误与时区错误这个坑在环境搭建阶段几乎必现。错误信息通常是这样的Establishing SSL connection without servers identity verification is not recommended.一句话解释MySQL 8默认开启SSL但本地开发环境的驱动不知道要不要校验服务器的身份于是抛了个警告有些版本直接报错拒绝连接。解决方式就是我前面提到的那串连接参数useSSLfalse直接关掉SSL验证。如果你看到的是serverTimezone相关的报错就加上参数改成Asia/Shanghai。这两个坑同时出现的时候控制台会打得一片红别慌先检查URL是不是两个参数都带了。5.3 JSP页面EL表达式透传字符串在JSP页面里用${user.username}可以拿到后台用户对象。但有一种情况会让页面动态数据全部不生效——就是JSP顶部没有加% page isELIgnoredfalse %Servlet 3.0以后web.xml里如果配置的servlet版本是2.4以下EL表达式默认是忽略的于是页面上所有${...}都会原封不动显示成字符串。遇到这种问题别急着怀疑后台数据没传对先确认web.xml顶部web-app标签的schema版本是不是3.0或更高再确认page指令里没写isELIgnoredtrue。这是一个很基础的坑但走查时经常被忽略。5.4 MyBatis分页的total和list对不上手写分页时很多人会图方便把“查列表”和“查总数”拼成一条SQL用select count(*)和limit同时返回。这样看起来省事可一旦列表查询里带了多表joincount统计就会被join出来的笛卡尔积干扰总数偏大。正确做法是分开两条SQL一条专门select count(*) from ...按主表维度计数另一条才是分页列表查询。如果引入了PageHelper请务必确保PageHelper.startPage紧跟你要分页的那条查询别在中间插入其他查询否则它会把SQL塞到错误的语句后面导致数据错乱。5.5 IDEA里Tomcat部署的各种姿势用IDEA跑传统JSP项目不要用Jrebel老套的“懒人部署”直接说最稳的方式在IDEA右上角配置一个Tomcat ServerDeployment那里把项目的artifact选成war exploded模式爆炸目录部署Application context填/mall。这种模式的好处是修改JSP页面后不需要重启Tomcat刷新页面就能生效——注意这里我说的是JSP修改如果你改了Java代码还是要重新编译并让IDEA触发热部署否则看到的是旧class。另一个高频报错是Artifact mall:war exploded: Error during artifact deployment.这种多半是webapp目录下XML文件写错了或者web.xml的schema版本配置错误。先去IDEA的Run窗口看详细异常栈会提示具体是哪个配置文件的哪一行解析失败不要一上来就怀疑Tomcat坏了。Tomcat卸载重装解决不了代码写错的问题这个问题我见过太多次了。5.6 后台管理页面跳转出现“JSP only supports”报错还有一种情况当你把JSP放到WEB-INF/pages/下面之后Controller返回逻辑视图名比如返回admin/product视图解析器拼成的路径是/WEB-INF/pages/admin/product.jsp。如果这个目录下没有product.jsp文件页面会报一个很迷惑的404。我的排查习惯是直接看返回字符串和真实目录结构是否完全对应尤其注意大小写和文件夹层级。很多人在这里栽跟头不是代码逻辑问题就是纯粹的文件放错位置了。6. 运行与部署从IDEA跑通到Tomcat发布6.1 本地环境准备清单在你亲手搭建这套SSM商城系统之前先把环境对齐。我建议统一用下面这套组合兼容性经历过大量验证软件推荐版本关键配置JDK1.8不要用JDK 17以上跑传统SSM很多老依赖不支持MySQL5.7 或 8.0字符集utf8mb4端口3306Tomcat8.5 或 9.0HTTP端口8080IDEIntelliJ IDEA自带Maven支持Maven3.6.x配置阿里云镜像加速如果你用的是MySQL 8.0JDBC驱动务必选mysql-connector-java 8.0.x不要用老版5.x驱动否则无法建立连接。如果用的是MySQL 5.7那驱动用5.1.49完全够。JDK版本这里特别强调一下很多SSM框架的CGLIB代理、旧版Spring在JDK 8以上都会出现反射报错所以老老实实用JDK 8你后面会少掉一大半报错。6.2 IDEA里从零导入并配置JavaWeb项目假设你拿到的是一份SSM商城的源码zip正确的导入步骤是File → Open选pom.xml → 选择以Maven项目方式导入。等待依赖下载完成后Project Structure里检查项目的Java SDK是否指向1.8。然后配置运行环境Run → Edit Configurations → 点号选择Tomcat Server → Local。Application server选择本地Tomcat安装目录。Deployment选项卡里点号选择Artifact选中mall:war exploded。Application context填成/mall这样本地访问地址就是http://localhost:8080/mall。Server选项卡里On Update action设置为Update classes and resources避免改页面还要重启Tomcat。这个配置写一遍后面跑任何JavaWeb项目都能复用。很多人跑到这一步才开始问“为什么我的IDEA里没有Tomcat选项”那多半是还没配置Application server的Tomcat路径或者IDEA版本太老。IDEA里配置好Tomcat路径后那个绿色的三角形旁边才会出现下拉框。6.3 打包war发布到服务器课程设计如果能成功部署到一台云服务器上展示效果会好很多。打包方式很简单在IDEA右侧Maven面板双击packagetarget目录下会生成mall.war。把war包复制到服务器Tomcat的webapps目录下启动Tomcat它会自动解压war包。访问http://服务器IP:8080/mall/就能看到商城首页。有几件事打包前必须检查pom.xml里packagingwar/packaging别漏了。数据库连接URL里的host改成服务器的IP不要写localhost。JSP页面里所有路径都写成${pageContext.request.contextPath}开头不能是写死的/mall否则你本地部署和服务器部署只要context path不一样所有链接都会挂。数据库初始化SQL要先在服务器MySQL里执行并创建好对应的库和账号权限。服务器上用RPM方式装MySQL或直接yum install都行但装完一定要检查端口3306是否在防火墙里放行云服务器安全组也要同步放开不然程序连不上数据库页面报错只会显示“Cannot create PoolableConnectionFactory”。6.4 上线前的最小优化课程设计也好内网小项目也好上线前我建议做三件性价比最高的事第一修改applicationContext.xml里的数据库密码不要用简单密码至少别和本地开发环境一个密码。第二给密码字段加存储前的哈希处理这个在上面登录模块里已经说过了。第三打开MyBatis的SQL日志输出生产环境可以关闭但调试阶段留着它你能直接在控制台看到每条SQL的执行情况排查慢查询和N1问题都靠它。这三个动作半小时就能完成但对“项目完整度”的加分作用是实打实的。尤其是SQL日志你给老师或面试官演示的时候别人一眼就能看出你是真的把项目跑通了而不是对着截图背稿子。7. 这套系统跑通之后你还可以往哪里扩展项目跑通只是开始。如果你想把这个SSM商城系统真正写进简历或者想从里面榨出更多学习价值我给出几个方向按投入产出比排序第一加一个简单的Redis缓存缓存首页热门商品列表。你会发现引入缓存之后商品详情页的响应速度肉眼可见地变快同时也把“缓存穿透、缓存雪崩”这些面试词落到了实处。第二把后台的商品图片上传功能从本地磁盘存储改造成对接OSS对象存储学会处理文件上传和URL回填这对“完整项目”的含金量提升比再加十个功能都大。第三改进登录模块把JSP里直接存的user对象替换成Token机制或者接入Shiro做权限控制这样可以进一步理解权限框架和Shiro的Filter链是怎么和Spring容器整合的。如果时间和精力有限我建议优先做第一件。理由很简单加Redis缓存不需要改动表结构也没有复杂的页面改动你只需要在Service查询商品列表前查一次Redis再考虑一下缓存过期时间属于“改完马上就能演示效果”的典范。等你真正动手做完你会发现SSM这套体系在你脑海里已经不再是“框架名列表”而是一整套有血有肉的数据流转路径。到那时候你再转头去看Spring Boot自动配置很多原先觉得神奇的东西其实本质都是老朋友换了个更省事的外观而已。最后再分享一个小技巧把这个项目里的JSP页面逐个截图、把核心表结构画成ER图、把一次完整下单流程的时序写清楚整理成一篇带图的博客发出来。这一套整理下来你对这个项目的理解会再上一个台阶而且它也会成为你后续面试自我介绍时最拿得出手的作品。
返回列表