新闻详情

新闻详情

首页 / 资讯中心 / 详情

购物车开发中的一个代码片段学习

发布时间:2026/9/28 20:30:58来源:尧图网络
购物车开发中的一个代码片段学习
/** * 删除购物车中一个商品(sub) */ Override public void subShoppingCart(ShoppingCartDTO shoppingCartDTO) { ShoppingCart shoppingCart new ShoppingCart(); BeanUtils.copyProperties(shoppingCartDTO, shoppingCart); shoppingCart.setUserId(BaseContext.getCurrentId()); ListShoppingCart list shoppingCartMapper.list(shoppingCart); if (list ! null !list.isEmpty()) { ShoppingCart cart list.get(0); //这一行是整个方法的分水岭:从这里开始,cart 才是要动的那条数据,前面的 shoppingCart 已经完成它的使命了。 if (cart.getNumber() 1) { shoppingCartMapper.deleteById(cart.getId()); // 只剩1个再减 → 整行删掉 } else { cart.setNumber(cart.getNumber() - 1);//还有多个数量减1 shoppingCartMapper.updateNumberById(cart);//更新回数据库 } } // 车里没有这件商品 → 什么都不做(静默) }这段代码看着绕,其实是因为里面有两个ShoppingCart对象——一个条件、一个结果。这是你困惑的核心。先把这一层拆开,后面就全通了。一、两个对象分别是什么?(关键!)java复制ShoppingCart shoppingCart new ShoppingCart(); // ← 对象① BeanUtils.copyProperties(shoppingCartDTO, shoppingCart); shoppingCart.setUserId(BaseContext.getCurrentId()); ListShoppingCart list shoppingCartMapper.list(shoppingCart); ShoppingCart cart list.get(0); // ← 对象②(完全不同的东西!)对象①shoppingCart对象②cart是什么查询条件(一张搜索纸条)查出来的那条数据(数据库里的行)有哪些值userId、dishId、dishFlavor全部字段都有,包括最关键的id谁造的你自己new的MyBatis 从 ResultSet 映射出来的用途传给list()当筛选条件后面要改它 / 删它第①个对象的作用,相当于你在这个搜索框里输入:查找:用户4的购物车里,dish_id4、口味不辣的那条记录而第②个对象是搜索结果那一条记录,它带着数据库的主键id(比如 id18)。为什么要这个 id?因为后面无论是数量减 1还是删掉整行,都必须精确告诉我 改哪一行:sql复制update shopping_cart set number 2 where id 18 ← 靠 id 定位那一行 delete from shopping_cart where id 18 ← 靠 id 定位那一行⚠️注意:对象①里没有 id!它是你 new 出来的空对象,只填了三个字段。所以不能用shoppingCart.getId()去更新—— 那是 null,where id null什么都改不了(而且不报错,静默失败)。二、把代码翻译成白话复制① 准备一张搜索纸条:我是谁(userId) 哪道菜(dishId) 什么口味(dishFlavor) ② 拿这张纸条去数据库找这一条记录 ③ 找不到 → 直接结束(什么都不做) ④ 找到了(只可能有一条): ├─ 它只剩 1 个了 → 把整行删掉 └─ 否则 → 数量减 1,更新回去就这么四步。下面是逐行对照。三、逐行拆解第 1~3 行:造搜索纸条java复制ShoppingCart shoppingCart new ShoppingCart(); // 空对象 BeanUtils.copyProperties(shoppingCartDTO, shoppingCart); // 把 DTO 的同名字段拷过来 shoppingCart.setUserId(BaseContext.getCurrentId()); // 补上我是谁BeanUtils.copyProperties(源, 目标)—— 方向要记牢:第一个是源,第二个是目标(顺序反了就是从空对象拷到 DTO,什么都不会发生)。你的ShoppingCartDTO只有 3 个字段:java复制private Long dishId; private Long setmealId; private String dishFlavor;拷贝之后,shoppingCart里dishId/setmealId/dishFlavor有值了(前端传了哪个就有哪个,没传的是 null)。然后setUserId是必须的—— 因为 DTO 里没有 userId,前端也不传(上次讲过:身份从 token 来,不能信前端)。BaseContext.getCurrentId()就是从 ThreadLocal 里取出拦截器存进去的登录用户 id。第 4 行:查java复制ListShoppingCart list shoppingCartMapper.list(shoppingCart);复用的还是那条动态 SQL(上一轮讲的),生成的 SQL 是:sql复制select * from shopping_cart where user_id 4 and dish_id 4 and dish_flavor 不辣为什么返回的是List而不是一个对象?因为 Mapper 是通用查询——条件不同,结果可能是 0 条、1 条、多条,所以统一用 List 装。这里因为条件足够精确(userId dishId dishFlavor),最多只会有一条。第 5 行:判一条都没有java复制if (list ! null !list.isEmpty()) {list ! null !list.isEmpty()拆开看:判断含义为什么写list ! null集合对象本身存在防御性写法(MyBatis 正常不会返回 null,但养成习惯)!list.isEmpty()里面一条都没有这才是真正在判断我的车里有没有这道菜这里是静默返回的设计:车里没这道菜时,不抛异常、不报错,什么都不做。为什么静默是对的?场景:用户在界面上飞快连点两下减。第一次请求:把最后 1 个删掉了;第二次请求到达时,那行已经不存在了 → 查到 0 条。如果这时抛异常,用户会看到一个莫名其妙的报错,而他只是正常操作。安静结束才是对的。这在计算机里有个名字叫幂等(idempotent):同一个操作执行一次和执行两次,结果一样。减一个不存在的东西,结果仍然是它不存在,语义上是对的。第 6 行:取出那一条数据java复制ShoppingCart cart list.get(0);get(0) 第一条(Java 从 0 开始)拿到的是数据库那行的完整对象,包括id18、number3、name、amount等这一行是整个方法的分水岭:从这里开始,cart才是要动的那条数据,前面的shoppingCart已经完成它的使命了。第 7~11 行:真正的三岔路口java复制if (cart.getNumber() 1) { shoppingCartMapper.deleteById(cart.getId()); // 只剩 1 个 → 再减就没了 → 删整行 } else { cart.setNumber(cart.getNumber() - 1); // 还有多个 → 数量减 1 shoppingCartMapper.updateNumberById(cart); // 更新回数据库 }为什么要分两种?用你真实的数据看最清楚你库里现在有:复制id9 鮰鱼2斤 不辣 number3 id11 米饭 NULL number2用户点减:情况number走哪个分支结果减鮰鱼2斤(不辣)3elseupdate set number2 where id9→ 行还在,数量 2减米饭到只剩1个时再减1ifdelete where id11→整行消失为什么数量 1 的时候要删掉整行,而不是把它更新成 0?因为数量为 0 的行是垃圾数据:界面难看:购物车列表里会显示米饭 ×0,用户还得自己去删;逻辑会绕:下次用户再加米饭时,add里查到这条 number0 的行,会执行011的 update —— 虽然碰巧能用,但语义上很别扭;数据会累积:用户每点一次加到0就留一条僵尸行,时间久了表里一堆没用的记录。所以减到 0 就等于删除是最干净的处理。cart.getNumber() 1为什么能这样写?getNumber()返回的是Integer(包装类型),而1是int字面量(基本类型)。一边是基本类型 → 会自动拆箱成数值比较 → 安全✓对比一下你在套餐里踩过的坑:java复制StatusConstant.ENABLE setmeal.getStatus() // ❌ 两边都是 Integer → 比对象地址!分界线记住:一边是基本类型(int) →安全两边都是包装类型(Integer) → 必须用.equals()updateNumberById(cart)为什么传整个对象?因为它的 SQL 需要两个字段:java复制Update(update shopping_cart set number #{number} where id #{id}) void updateNumberById(ShoppingCart shoppingCart);#{number}→ 要改成的新值(刚减过的)#{id}→定位哪一行(从数据库查出来的)而cart恰好两个都有 ✓ 这正是为什么要用查出来的cart,而不是自己 new 的那个的原因。四、四种状态全览(这张表就是全部逻辑)购物车里的状态list返回走哪条路执行什么 SQL最终结果鮰鱼 × 31 条elseupdate ... set number2 where id9行还在,数量 2米饭 × 11 条ifdelete ... where id11整行消失车里没这道菜0 条if 不成立无什么都不做连点两次减(第二次已删)0 条if 不成立无不报错✓最后一行最能说明设计水平—— 它不报错、不弹窗,安静地接受你要减的东西已经没了。五、这套套路你能复用到别处凡是**改数量/改状态**的需求,基本都是这三步:复制① 构造条件 → 查(拿到那条数据的完整对象,尤其要它的 id) ② 查不到 → 静默结束(或按业务抛异常) ③ 查到了 → 判断边界(是不是最后一个/最后一个状态) ├─ 是 → 删除 / 特殊处理 └─ 否 → 正常修改每次看到1/-1,先问自己一句:到边界会怎样?这个习惯能帮你躲掉一半的业务 bug。六、补充一个更专业的对比(了解即可)其实这段逻辑可以只用两条 SQL 完成,不需要先查:sql复制-- 还有多个时,直接减 update shopping_cart set number number - 1 where user_id ? and dish_id ? and dish_flavor ? and number 1; -- 只剩最后一个时,直接删 delete from shopping_cart where user_id ? and dish_id ? and dish_flavor ? and number 1;用受影响行数判断该走哪条。好处:少一次查询、并发下更安全(两个人同时减,不会都读到 3 然后都写 2,导致少减一次)。课程选先查后改是因为:① 逻辑直观、和add对偶 ② 教学上好理解 ③ 学习阶段不涉及并发。你知道有这么个更优解就行,现在按课程写完全没问题。一句话总结subShoppingCart的本质是:减一个之前先搞清楚我在减第几个**——**· 还有好几个(1)→ 改数字· 是最后一个(1)→ 删整行· 根本没有(查不到)→ 什么都不做而查这一步,靠的是userId dishId dishFlavor三个条件精确定位,其中userId来自 token(保证只动自己的车)。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Substrate:云原生可信执行环境的核心基建范式 2026/9/28 22:54:57

Substrate:云原生可信执行环境的核心基建范式

1. Substrate 是什么:不是区块链框架,也不是“另一个 Rust 库”,而是可验证执行环境的底层基建范式Substrate 这个词在当前技术语境里,正经历一场静默但剧烈的语义迁移。它早已脱离最初作为 Polkadot 生态链构建框架的单一定义&am…

阅读更多 →
模板代码模块化设计:从背代码到拼插槽,考场不再失灵 2026/9/28 22:54:57

模板代码模块化设计:从背代码到拼插槽,考场不再失灵

1. 为什么积累的模板一到考场就失灵:问题出在"块"上先说个我观察了很久的现象。很多备考计算机考研408、或者准备算法面试的朋友,电脑里都躺着一个名为"模板"的文件夹,里面要么是《算法模板大全.pdf》,要么是…

阅读更多 →
Hive本地模式与集群模式:判定机制、参数配置与踩坑实战 2026/9/28 22:54:57

Hive本地模式与集群模式:判定机制、参数配置与踩坑实战

凌晨一点,我在工位上盯着一只跑了一个小时还没出结果的Hive SQL,一度怀疑集群被拉满。后来发现这条SQL扫描的表只有不到200MB,卡住它的不是数据量,而是执行模式的选择出了问题——它被当成一个普通集群任务提交到YARN,…

阅读更多 →
MySQL主从复制实战:GTID、binlog配置与故障排查指南 2026/9/28 22:54:57

MySQL主从复制实战:GTID、binlog配置与故障排查指南

搞主从配置的文档满网都是,但大多数都是“照官方文档抄一遍,能通就行”的水平。我今天写这篇,不是又给你贴一遍CHANGE MASTER TO,而是想把我实际在生产环境折腾MySQL主从的经验、踩过的坑、还有怎么排查的思路一次性整理出来。尤其…

阅读更多 →
客户流失预测实战:数据理解与特征编码全攻略 2026/9/28 22:54:57

客户流失预测实战:数据理解与特征编码全攻略

客户流失预测是机器学习在业务侧最经典的落地场景之一。不管你是做电信、银行、SaaS还是电商,只要业务有存量用户,就绕不开“哪些用户快要走了”这个问题。我这次想分享的是一个完整的客户流失预测项目的上半部分:项目背景梳理和特征编码&…

阅读更多 →
基于MFC的扫雷程序设计:架构、算法与避坑实践 2026/9/28 22:54:51

基于MFC的扫雷程序设计:架构、算法与避坑实践

简介:面向MFC初学者的扫雷程序设计完整项目,包含经典扫雷核心逻辑与仿Windows扫雷界面,可直接运行体验。项目采用Visual Studio工程组织,涵盖对话框、视图、文档等MFC典型架构,适合正在学习Windows应用程序开发、需要课…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉