恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

JavaWeb购物车系统实现:基于Session存储的完整工程示例

  • 首页
  • 资讯中心
  • /
  • JavaWeb购物车系统实现:基于Session存储的完整工程示例

相关资讯

Apache Druid Timeseries 原生查询完全指南:查询结构、参数详解与源码级实现原理 2026/9/23 23:57:20
Vue3 后台如何做权限管理?V3 Admin Vite 页面级 + 按钮级权限双维度方案完全指南 2026/9/23 23:57:20
OpenSpec:规范驱动开发(Spec-Driven)的契约编译器与双向同步实践 2026/9/23 23:57:20

最新资讯

C++与OpenCV实现光学相位测量技术:相移法与三频外差法
Java开发环境搭建与Tomcat配置实战指南
ECG心电信号分类实战:Python与Matlab双版本实现与避坑指南
YOLOv7打电话检测实战:双格式数据集与训练部署全解析
ResNet50迁移学习做垃圾分类:数据对齐、模型改造与可解释性实战
AI Code Review架构实战:从Git Diff到大模型的分层编排与上下文工程

今日推荐

JavaWeb购物车系统实现:基于Session存储的完整工程示例
面向对象综合训练:从图书管理系统掌握封装、继承与多态
Lombok与JDK版本冲突引发NoSuchFieldError:根因排查与修复指南

本周热门

BrewUI:给Homebrew套上图形界面,让macOS软件包管理更简单
BrewUI:让Homebrew包管理变得可视化与高效
公式与文本对齐全攻略:从Word到LaTeX的实用技巧

本月精选

自研推理加速器Redwood:两周内实现PyTorch模型高效部署的实战教程
V4L2摄像头采集实战:从camera_client.rar到出图全流程解析
从“谁发明了钢琴键”到知识问答智能体:RAG与记忆工程实践

JavaWeb购物车系统实现:基于Session存储的完整工程示例

发布时间:2026/9/24 0:02:20
JavaWeb购物车系统实现:基于Session存储的完整工程示例 简介这是一份面向Java Web初学者的简易购物车系统案例完整演示了基于Servlet与Tomcat的商品选购流程案例来自课程设计或实验场景需求中要求设计商品展示页面点击“添加到购物车”超链接后进入Servlet记录选购信息再跳转至另一个Servlet用表格展示购物车内容非常适合作为入门阶段的练手项目。工程中包含商品展示页面、两个职责不同的Servlet以及配套的HTML和CSS样式第一个Servlet处理添加请求存储商品名称、单价和选购数量第二个Servlet以表格方式展示当前购物车明细整体逻辑清晰便于理解请求转发、参数传递与结果回显。资源包共25个文件压缩后仅491KB主要包含3个Java源码、3个编译后的class文件、1个商品页HTML、8张商品配图以及Eclipse工程配置和Web部署描述文件项目目录结构完整导入IDE后可直接部署到Tomcat运行方便对照学习。当前已有638人学习下载适合正在练习Servlet表单处理、会话维持和Web页面跳转的初学者也适合作为课堂实验或课程设计的入门参考项目代码风格简洁关键步骤有注释可在此基础上继续扩展用户登录、订单结算等功能。1. 购物车系统的第一课它本质上只是Session里的一个Map很多第一次做javaWeb课程设计的同学拿到“购物车系统”这个题目第一反应是建一张购物车表然后在MySQL里加车、减车、清空轮流转。这是最典型的先入为主——实际上一个基于servlet的简单购物车系统根本不需要购物车表它只是把一个Map挂在了HttpSession上key是商品IDvalue是包含商品信息和数量的购物项会话结束Map销毁购物车自然清空。理解了这一点整个项目的复杂度会降一个量级。这篇文章会从数据设计、工程搭建到核心代码、常见翻车点完整告诉你一个可运行、可答辩的购物车系统是怎么从零写出来的。2. 技术选型与数据设计ServletJSP为什么是JavaWeb教学案例的常青树2.1 购物车数据到底存哪Session、Cookie、MySQL三选一购物车的数据归属从产品层面看有两种游客临时选品和登录用户跨端同步。作业级的简单购物车系统服务的显然是前者所以数据存放位置的优劣排序非常明确存储位置生命周期优点典型问题HttpSession会话结束即销毁实现最简单内存读写快天然隔离用户重启或过期数据丢失Cookie按过期时间走可跨请求保持轻量大小限制4KB用户可改有安全风险MySQL表永久永久保存可统计需要用户体系CRUD成本高我见过很多同学固执地把购物车设计成一张表为了填user_id还得先做登录模块做了登录又得做注册——最后课程设计的大部分时间都花在了跟购物车无关的地方。简单的购物车系统就该用Session这也是大部分JavaWeb教学案例的默认方案。2.2 分层结构JSP只负责展示Servlet只做调度很多初学者写Servlet代码时把JDBC查询、业务判断、页面跳转全堆在一个doGet里一个方法写上两三百行。短期看能跑但答辩时老师让你加一个“购物车满减”功能你就知道什么叫牵一发动全身。常见的做法是拆成三层视图层JSP只负责用EL表达式和JSTL渲染数据不写Java脚本。控制层Servlet接收请求参数调用Service设置request/session属性最后转发或重定向。业务与数据层Service DAOService处理“加购时数量合并”这类业务规则DAO只做SQL执行。这样拆完以后购物车的核心业务可以完全脱离Servlet做单元测试这是教学案例里很少被强调但面试时会被追问的东西。2.3 购物车核心数据结构MapString, CartItem是唯一正解购物车的数据结构设计决定了后续所有代码的写法。用一个ArrayList 也能“装”购物车但每次加入商品都要遍历查找是否已存在数量合并又得手动遍历两次代码丑陋且容易出bug。正确做法是定义一个Cart类内部维护一个Mappublic class Cart { // 外层传进来的商品对象在项目里建议从DB查出来后放入item // 避免用户通过改前端参数伪造价格 private MapString, CartItem items new LinkedHashMap(); // 加购时若商品已在购物车中只合并数量 public void add(CartItem item) { String key item.getProduct().getId(); CartItem exist items.get(key); if (exist ! null) { exist.setCount(exist.getCount() item.getCount()); } else { items.put(key, item); } } // 获取商品项列表JSP里直接遍历 public CollectionCartItem getItems() { return items.values(); } // 计算总价在JSP中用 ${cart.totalPrice} 直接取 public double getTotalPrice() { return items.values().stream() .mapToDouble(CartItem::getSubtotal) .sum(); } }CartItem则是对“商品 数量 小计”的封装public class CartItem { // 商品对象里至少包含 id、name、price、pnum // 注意这里存的是加入购物车那一刻的商品信息快照 private Product product; private int count; // 小计不设setter由商品价格和数量实时计算 public double getSubtotal() { return product.getPrice() * count; } }这里有两个关键点一是用Map而不是List加入商品的时间复杂度是O(1)避免遍历二是小计用getter实时计算而不是存一个字段防止改数量后忘记同步小计。这两点你写在答辩里“数据设计合理”这五个字就有着落了。我不想三件事购物车的商品信息一定要从数据库查全不能只查ID就塞进item。购物车系统的核心是SessionSession的生命周期需要理解透。2.4 数据库到底建几张表只建商品表就够了我们不建用户表、不建购物车表、不建订单表只建一张商品表。购物车在Session里结算时直接用购物车里的数据去扣商品库存。这是“简单购物车系统”和“完整商城系统”的本质区别。商品表字段设计有一个关键约定价格必须用DECIMAL不能用double或float。理由在避坑章节会展开讲这里先记住结论凡是涉及金额的字段一律DECIMAL(10, 2)。CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 商品ID, name VARCHAR(50) NOT NULL COMMENT 商品名称, price DECIMAL(10,2) NOT NULL COMMENT 商品单价, pnum INT NOT NULL DEFAULT 0 COMMENT 库存, description VARCHAR(255) DEFAULT COMMENT 描述 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品表;工程完成度更高一点的做法是再建一张登录用户表把Session里的购物车归属到一个userId上方便将来换成Redis购物车时做数据迁移。但如果你只是为了跑通项目商品表一张足矣。多建的表如果代码里没用到答辩时反而会被问得手足无措。3. 环境搭建用IDEA 2026 Maven Tomcat MySQL跑通最小可运行项目3.1 版本对齐是最容易翻车的一环Tomcat 9 JDK 17很多同学的javaWeb环境配置教程看了好几篇最后死在Tomcat 10上代码里明明写的是import javax.servlet.http.HttpServlet项目却一直报ClassNotFoundException折腾半天才发现Tomcat 10已经把javax改成了jakarta。下面这套版本组合是我实际用过、报错最少的一套可以放心照抄组件版本说明JDK17长期支持版本稳定Tomcat9.0.x最后一个javax.servlet版本和教材、网上的bug帖一致Maven3.8.xIDEA内置即可MySQL8.0 / 8.4用8.x驱动和连接串是配套写法IDEA2026新建项目时选择Maven骨架即可Tomcat用9.0.x这一点是血泪经验。Tomcat 10以后的Servlet API包名全换成了jakarta.servlet如果你跟着旧教程写代码依赖和Tomcat版本不对齐项目永远起不来。3.2 Maven项目骨架与pom.xmlwar包加两个依赖IDEA 2026新建项目时直接选“生成器”里的Maven骨架模板GroupId填com.exampleArtifactId填cart-demoPackaging选war即可。关键在于pom.xml里的依赖要对project packagingwar/packaging dependencies !-- 编译期需要Tomcat容器里已有实现类 -- dependency groupIdjavax.servlet/groupId artifactIdjavax.servlet-api/artifactId version4.0.1/version scopeprovided/scope /dependency !-- JSP页面里c:forEach标签的依赖 -- dependency groupIdjavax.servlet/groupId artifactIdjstl/artifactId version1.2/version /dependency !-- MySQL 8.x驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.33/version /dependency /dependencies /project有几个坑值得说明javax.servlet-api的scope必须设为provided因为Tomcat自带的Servlet实现比你的依赖版本要权威如果不写provided打包出来的war可能和容器冲突jstl这个东西是JSP页面里c:forEach这些标签的来源漏了它JSP会直接报错MySQL驱动的版本跟随你的MySQL版本8.x MySQL就用8.x驱动不存在“版本越新越好”的说法。3.3 IDEA运行JavaWeb项目配置Artifact和Tomcat LocalMaven结构建好之后还差最后一步让IDEA把项目跑在Tomcat上。这一步就叫“IDEA运行JavaWeb项目配置”很多同学卡在这里。操作步骤分三步打开Run/Debug Configuration新增一个Tomcat Server - Local配置。在Deployment页面点击加号选择Arifact切换为war explodedApplication context设置成/cart。在Server页面确认HTTP port是8080然后点启动按钮。这里有个最关键的细节IDEA默认创建的是war包但调试时需要的是war exploded模式即解压目录。区别在于war格式每次修改代码都要重新打包再部署改了Java代码就得等好几秒war exploded模式改完代码只要点击Build里的Rebuild就能立即生效调试体验好一个档次。这一点在教程里很少被单独提到。启动后浏览器访问http://localhost:8080/cart/product/list能看到商品列表页就说明环境通了。很多同学在配置VS Code编写Servlet代码怎么配置环境上折腾得很痛苦——不是不行只是JavaWeb项目调试涉及Tomcat端口、context路径、热部署三个维度VS Code都要手动接线IDEA帮你全做完了。既然是课程设计别在IDE上浪费时间。3.4 MySQL建库建表与初始商品数据入库数据库连接串建议固定用下面这套写法它把编码和时区问题一次解决了jdbc:mysql://localhost:3306/cart_db?useSSLfalseserverTimezoneAsia/ShanghaicharacterEncodingUTF-8其中characterEncodingUTF-8是中文乱码的第一道防线serverTimezoneAsia/Shanghai是MySQL 8.x驱动连不上时的报错CLIENT_PLUGIN_AUTH或Server returns invalid timezone的解法useSSLfalse是省去本地调试时证书警告。初始化数据直接用下面的SQL一次灌进去就好INSERT INTO product (name, price, pnum, description) VALUES (Java核心技术 卷I, 89.90, 100, Java入门经典书籍), (Spring实战 第6版, 108.00, 80, Spring框架应用), (深入理解Java虚拟机, 126.80, 50, JVM进阶必读), (Maven实战, 66.50, 60, 构建工具入门);一个需要注意的细节是pnum字段不要给0否则待会加购成功但是结算时库存永远不够容易误判代码逻辑有问题。4. 手写购物车核心代码商品列表、加入、改数、删除、清空4.1 商品列表页JSP用JSTL循环不要在页面里写Java脚本商品列表页是购物车系统的门面。很多教材里教学生用% for(...) { %这种JSP脚本片段去遍历商品这种做法在JavaWeb教学案例里已经过时了项目的可维护性很差页面里混着一堆Java代码改动布局时处处受制。正确做法是Servlet把商品列表放进requestJSP用EL表达式和JSTL取出来。先写ProductServlet负责查全表并转发到JSPWebServlet(/product/list) public class ProductListServlet extends HttpServlet { private ProductService productService new ProductService(); Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // ListProduct 是核心JSP只做展示 req.setAttribute(products, productService.queryAll()); req.getRequestDispatcher(/product_list.jsp).forward(req, resp); } }service层直接查商品表即可public ListProduct queryAll() { String sql SELECT id, name, price, pnum FROM product; // 用JDBC或工具类执行sql把每行映射成Product对象放入List return jdbcUtil.queryList(sql, Product.class); }JSP页面里的核心循环只有三行c:forEach items${products} varp tr td${p.name}/td td${p.price}/td tda href${pageContext.request.contextPath}/cart/add?productId${p.id}count1加入购物车/a/td /tr /c:forEach${pageContext.request.contextPath}是动态获取应用上下文路径的写法这样无论context path配置成/cart还是/root链接都不会写死项目部署到别的环境不会断链。如果把路径写死成/cart/cart/add换个环境就全部404。4.2 加入购物车CartServlet加一个加法方法加入购物车是购物车系统的核心入口它的逻辑流程是取Session中的购物车对象没有则新建根据前端传的商品ID从数据库查出商品封装成CartItem调用Cart的add方法合并数量最后重定向回商品列表页。WebServlet(/cart/add) public class CartServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String productId req.getParameter(productId); // 项目里约定count参数可以缺省缺省时默认加1件 int count 1; String countParam req.getParameter(count); // 前端数据不可信解析失败直接按1处理 if (countParam ! null countParam.matches(\\d)) { count Integer.parseInt(countParam); } Cart cart getCart(req); ProductService service new ProductService(); // 商品信息要以DB查询为准不能用前端传来的价格或名称 Product product service.findById(productId); if (product null) { resp.sendError(404, 商品不存在); return; } CartItem item new CartItem(); item.setProduct(product); item.setCount(count); cart.add(item); // 重定向而不是转发避免用户刷新页面时重复提交 resp.sendRedirect(req.getContextPath() /product/list); } private Cart getCart(HttpServletRequest req) { HttpSession session req.getSession(); Cart cart (Cart) session.getAttribute(cart); if (cart null) { cart new Cart(); session.setAttribute(cart, cart); } return cart; } }这段代码里有几个参数层面的考虑。第一个是count参数的校验用户完全可以通过改URL参数把count传成“-5”或者“a”所以要先做正则校验再做类型转换任何一步不过都按默认值兜底。第二个是末尾用sendRedirect而不是forward转发如果用了forward用户刷新商品列表页时表单数据会原样再次提交购物车里的商品数量会翻倍这是典型的”重复提交“问题。第三个是getCart方法的抽取因为后面修改数量、删除、清空等操作都要先在Session里拿到购物车对象抽成一个单独方法代码就不会到处重复。4.3 购物车页面修改数量、删除、清空一次写完购物车页面对应的Servlet是CartServlet的三个分支update、remove、clear。这里不做URL路由框架直接在同一个Servlet里按路径后缀区分动作代码结构更清晰WebServlet(/cart/*) public class CartServiceServlet extends HttpServlet { Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String path req.getPathInfo(); // 形如 /update, /remove, /clear if (/update.equals(path)) { update(req, resp); } else if (/remove.equals(path)) { remove(req, resp); } else if (/clear.equals(path)) { clear(req, resp); } else { resp.sendError(404); } } Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 表单用POST提交时统一交给doGet逻辑处理 doGet(req, resp); } private void update(HttpServletRequest req, HttpServletResponse resp) throws IOException { String productId req.getParameter(productId); String countParam req.getParameter(count); // 修改数量时前端可能传0或负数这里直接拒绝 int count 0; if (countParam ! null countParam.matches(\\d)) { count Integer.parseInt(countParam); } if (count 0) { // 数量非法等价于从购物车移除 remove(req, resp); return; } Cart cart getCart(req); ProductService service new ProductService(); Product product service.findById(productId); // 以数据库中最新价格为基准重新生成购物项 cart.update(product, count); resp.sendRedirect(req.getContextPath() /cart.jsp); } private void remove(HttpServletRequest req, HttpServletResponse resp) throws IOException { String productId req.getParameter(productId); Cart cart getCart(req); cart.remove(productId); resp.sendRedirect(req.getContextPath() /cart.jsp); } private void clear(HttpServletRequest req, HttpServletResponse resp) throws IOException { req.getSession().removeAttribute(cart); resp.sendRedirect(req.getContextPath() /product/list); } }注意update方法里我做了两层防护第一层是count非法且小于等于0时直接转给remove处理把“数量改成0”这个语义等价为“移除商品”这是电商系统里很常见的产品逻辑第二层是重新查库拿到最新商品信息再去update而不是直接用购物车里的旧对象——避免商品在后台改了价格后购物车里的价格还是旧值。Cart类里对应要补上两个方法public void update(Product product, int count) { CartItem item items.get(product.getId()); if (item ! null) { item.setCount(count); } } public void remove(String productId) { items.remove(productId); }整个购物车页面的JSP用一张表格展示每行是一个购物项最后一列是操作按钮c:forEach items${sessionScope.cart.items} varitem tr td${item.product.name}/td td${item.product.price}/td td form action${pageContext.request.contextPath}/cart/update methodpost input typehidden nameproductId value${item.product.id} input typenumber namecount value${item.count} min1 button typesubmit更新/button /form /td td${item.subtotal}/td td a href${pageContext.request.contextPath}/cart/remove?productId${item.product.id}删除/a /td /tr /c:forEach tr td colspan4合计${sessionScope.cart.totalPrice}/td tda href${pageContext.request.contextPath}/cart/clear清空购物车/a/td /tr表单的action指向/cart/update用POST提交。${sessionScope.cart}从会话里取购物车注意这里写的是sessionScope——购物车在Session里不在request里新手最容易在这里取空值导致页面上什么都显示不出来。4.4 结算的简化实现扣库存这一行代码要写对简单购物车系统的“结算”不需要生成订单表常见做法是遍历购物车里的每个购物项逐项扣减商品表的库存然后清空Session中的购物车。核心SQL长这样UPDATE product SET pnum pnum - ? WHERE id ? AND pnum ?;最后这个AND pnum ?非常关键。它可以做到在数据库层面防止超卖如果库存只剩5件但购物车里有10件这条UPDATE的执行影响行数会返回0代码里判断affectedRows 0就说明库存不足可以给出提示而不是悄悄扣成负数。这个写法在并发场景下也能扛住一部分压力是教科书里不会细讲但实际项目中一定会用的模式。结算后的处理public void checkout(Cart cart) { for (CartItem item : cart.getItems()) { int rows updateStock(item.getProduct().getId(), item.getCount()); if (rows 0) { throw new IllegalStateException(商品库存不足 item.getProduct().getName()); } } // 全部扣减成功购物车才会被清空 cart.clear(); }“全部扣减成功才清空购物车”这句注释不是废话——如果把clear放在循环前面万一中途某个商品库存不够抛了异常购物车已经被清了用户只剩下一个莫名其妙的报错页连重试的机会都没有。这个顺序问题在答辩时很容易被老师追问。5. 避坑购物车系统最常见的5个翻车现场5.1 现象Tomcat启动后JSP里的类全报ClassNotFoundException项目起不来这个坑几乎每个JavaWeb初学者都会踩一次。原因很简单Tomcat 10以后的版本已经把Servlet API的包名从javax.servlet换成了jakarta.servlet而你代码里import的还是javax.servlet.http.HttpServlet容器里根本没有这个类。解决直接换用Tomcat 9.0.x这是javax.servlet包名最后的支持版本。或者你把代码里的import全部改成jakarta开头的包名但那样网上大部分教程和答疑帖都对不上号了。我建议直接换Tomcat 9省心。5.2 现象页面上显示正常但数据库里insert的中文全部变成问号这种“玄学”问题的根源是编码链路断了一环。JavaWeb项目的编码链路是JSP文件编码 - 浏览器URL编码 - Servlet接收参数解码 - 数据库连接解码四个环节只要有任何一个不是UTF-8中文就会在某处变成乱码。解决一次治断根分三层对齐。第一层IDEA右下角把项目编码、属性文件编码全设成UTF-8第二层在所有Servlet开头加一行request.setCharacterEncoding(UTF-8)第三层JDBC连接串里带上characterEncodingUTF-8就像第3章里写的那样。三处全部到位乱码从此消失。另外Tomcat 9的conf/server.xml里如果Connector配置了URIEncoding保证它是UTF-8而不是ISO-8859-1。5.3 现象点一次“加入购物车”刷新页面后商品数量变成了2件、4件这是重复提交问题。原因是你的加入购物车功能用了forward转发而不是sendRedirect重定向浏览器刷新时把上一次请求又发了一遍数量就重复加了。解决所有修改购物车状态的Servlet最后一步必须是resp.sendRedirect跳转到GET页面。这就是Post-Redirect-Get模式把“写操作”和“页面展示”拆成两个单独的请求。写了这个模式之后用户随便怎么刷新购物车都不会变化。5.4 现象把数量改成0或者-3商品还在购物车里总价还算错这就是前端数据不可信的问题。很多同学以为JSP里写了input typenumber min1就万事大吉让用户通过浏览器改一下提交就能把count传成任何值。后端如果没做校验购物车里就会出现负数数量的商品总价也变成负数答辩时直接被扣分。解决后端拿到count参数后先做正则校验再判断数值范围非法数据要么拒绝要么降级。我在第4章update方法里给的策略是“数量小于等于0视为移除商品”这是一个比较优雅的兜底策略用户改了0不会报错购物车也会从“更新”变成“移除”产品逻辑自洽。5.5 现象结算时总价是0.30000000000000004商品价格用double类型存了这是金额用浮点类型的经典翻车。double在二进制里无法精确表示0.1这样的十进制小数累计多次运算后误差就暴露出来了。很多初学者图省事用double存价格结果结算金额出现一长串小数位。解决MySQL表字段用DECIMAL(10,2)Java代码里的对应字段用BigDecimal或者至少在展示时保留两位小数。JSP页面里可以配合fmt:formatNumber做格式化但那只是把显示上的尾巴去掉根治还得靠存储类型换成DECIMAL。6. 让购物车再进一步Session失效验证与两种提升方向6.1 用两个浏览器验证购物车的会话隔离写完购物车系统后第一件事是用两个浏览器比如Chrome和Edge同时打开商品列表页分别加入不同的商品然后互相看购物车。你会发现两个浏览器里的购物车互不干扰——这说明Session隔离是生效的。接着把某个浏览器关掉重新打开购物车是空的这说明Session的生命周期确实受控。这两条验证通过你的购物车系统核心逻辑就站得住了。6.2 两个提升方向Session兜底模板和Redis购物车如果还想让项目更有区分度有两个低成本提升点。第一个是给购物车页面加一个“Session可能过期”的提示模板当sessionScope.cart为空时显示“购物车已清空去逛逛”避免用户看到空白表格。第二个是给session的购物车加一个Listener监听HttpSessionListener的sessionDestroyed方法在会话销毁时打出日志方便答辩时演示。这两个改动都是几行代码的事但能体现出你对会话生命周期的理解。6.3 什么时候你才需要考虑换成Redis购物车做这个项目时我一再忍住不引入Redis——不是不会而是没必要。判断标准其实很简单当你的购物车需要支持“用户退出登录后购物车还在”“手机端加了车电脑端能同步”“大促时Tomcat集群需要共享Session”这三个场景之一时Session方案才会真正失效。到那时候再去接Rediskey直接设计成cart:{userId}value用Hash存商品ID和数量把第4章里Cart类的Map方法平移过去就行。在那之前ServletSession这套组合就是简单购物车系统的最优解。这是我做了几轮课程设计以后得到的教训希望帮到你。本文还有配套的精品资源点击获取

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号