
jpetstore-6購物車實現深扒HttpSession與Cart域模型設計的完整教程【免費下載鏈接】jpetstore-6A web application built on top of MyBatis 3, Spring 3 and Stripes項目地址: https://gitcode.com/gh_mirrors/jp/jpetstore-6jpetstore-6 是一個基于 MyBatis 3 與 Spring 3 構建的經典 Java Web 電商示例項目本文帶你完整拆解它的購物車實現購物車數據如何通過HttpSession在瀏覽器訪問間保持、Cart與CartItem這兩個Cart 域模型是如何設計的、以及從加購到下單的完整數據流。讀完你會發現一個看似簡單的購物車背后藏著懶加載、雙數據結構同步、BigDecimal 金額計算等一連串值得學習的工程細節 一、先看全貌購物車數據在系統里如何流轉整個購物車功能橫跨三層各自由獨立文件承載層次職責核心文件Web 層接收請求、讀寫 Sessionsrc/main/java/org/mybatis/jpetstore/web/controllers/CartController.java域模型層內存中維護購物車數據結構src/main/java/org/mybatis/jpetstore/domain/Cart.java、src/main/java/org/mybatis/jpetstore/domain/CartItem.java視圖層渲染購物車表格src/main/webapp/WEB-INF/jsp/cart/Cart.jsp數據流非常直觀每次請求 → 從 HttpSession 取出 Cart 對象 → 修改 → 隨 Session 保存 → 交給 JSP 渲染。購物車不經過數據庫全部存在服務器內存里直到用戶確認下單時才落庫。二、Cart 域模型深扒一個購物車兩套數據結構打開Cart.java最先映入眼簾的是兩個成員變量這是整個設計最精妙的地方private final MapString, CartItem itemMap new ConcurrentHashMap(); private final ListCartItem itemList new ArrayList();為什么要一份數據存兩份itemMapConcurrentHashMap以itemId為鍵實現 O(1) 的快速查找。加購前先查購物車里有沒有這件商品、按 ID 改數量、刪除商品全靠它。用并發容器也體現了對多線程 Web 環境的防御。itemListArrayList保持商品加入順序專門用于頁面展示和遍歷計算。兩套結構在每次寫操作時嚴格同步例如addItem()的邏輯是若itemMap中沒有該商品則新建CartItem并同時放入 map 和 list隨后統一調用incrementQuantity()。removeItemById()同理兩邊一起移除。 這種雙寫模式是購物車這類既要快查、又要保序場景的常見技巧。再看CartItem.java它是購物車的一行記錄只有 4 個字段item完整的商品對象來自domain/Item.java含價格、屬性、所屬產品quantity數量inStock加購時是否庫存充足的快照標記total小計金額一個容易忽略的細節是calculateTotal()——每次setItem、setQuantity、incrementQuantity都會自動用BigDecimal重算total listPrice × quantity。金額計算全程使用BigDecimal而非double避免了浮點誤差這是電商代碼的基本功。購物車總價getSubTotal()則用 Stream 一行流式求和itemList.stream() .map(ci - ci.getItem().getListPrice().multiply(new BigDecimal(ci.getQuantity()))) .reduce(BigDecimal.ZERO, BigDecimal::add);另外Cart和CartItem都實現了Serializable并顯式聲明serialVersionUID——因為 Session 在某些服務器配置下會把內容序列化到磁盤或內存換頁區不實現接口購物車就無法持久存活。三、HttpSession 中的購物車懶初始化模式CartController里有一段幾乎可以照抄到任何 Java Web 項目的代碼private Cart getCart(HttpSession session) { Cart cart (Cart) session.getAttribute(cart); if (cart null) { cart new Cart(); session.setAttribute(cart, cart); } return cart; }這就是懶初始化Lazy Initialization用戶還沒買任何東西時Session 里并不存在cart屬性第一次調用任意購物車接口時才創建并掛到 Session 上。好處是零浪費每個訪問者都只占一份內存。這里也暴露了 Session 方案的固有特性值得新手理解?匿名可用未登錄游客也能加購因為 Session 不依賴賬戶體系?讀寫極快純內存操作無需數據庫往返??單機限制多臺服務器做集群時Session 不共享購物車會丟??重啟即失應用重啟后內存中的購物車清空理解這些利弊你就知道為什么大型電商會改用 Redis 集中存儲購物車而 jpetstore-6 作為示例項目選擇 Session 是最務實的簡化。四、四個購物車操作接口逐個拆解CartController以RequestMapping(/cart)注冊共 4 個接口全部圍繞上面的getCart(session)展開1. 加購GET /cart/addItem參數為workingItemId。先做空值校驗再分兩條路徑商品已在車里cart.containsItemId()→ 直接incrementQuantityByItemId()新車里的商品 → 先經CatalogService.isItemInStock()查庫確認庫存再cart.addItem(item, isInStock)注意inStock是加購那一刻的快照之后庫存賣光購物車里仍保留該商品并顯示無貨用戶可以自行移除。2. 移除GET /cart/removeItem調用cart.removeItemById(itemId)若返回null說明車里根本沒這件商品走錯誤頁。3. 批量改數量POST /cart/update這是最接地氣的一個接口配合Cart.jsp中的表格——每行的數量輸入框name就是itemIdinput typetext size3 name${cartItem.item.itemId} value${cartItem.quantity} /服務端遍歷購物車用request.getParameter(itemId)按商品 ID 取回新數量并setQuantityByItemId()數量小于 1 就順手把該行刪除。遇到非法數字輸入則靜默跳過保證一個壞行不影響其他行。4. 去結算GET /cart/checkout僅僅把購物車塞進 Model轉發到cart/Checkout.jsp展示結算摘要真正的下單在下一節登場。五、從購物車到訂單一次教科書級的模型轉換購物車數據最終要在下單時物化成數據庫記錄這個轉換發生在OrderController.java與domain/Order.java之間分三步第一步登錄校驗 生成訂單草稿。GET /order/new要求accountBean已認證否則跳轉登錄頁隨后調用order.initOrder(account, cart)。第二步Cart → Order 字段搬運。Order.initOrder()完成三件事把賬戶里的收貨/賬單地址、姓名整塊拷貝到訂單totalPrice cart.getSubTotal()直接復用第二節的流式求和遍歷cart.getAllCartItems()把每個CartItem轉成LineItem行項目拷貝數量、商品 ID、單價并重算小計LineItemdomain/LineItem.java與CartItem結構高度相似——這正是購物車行與訂單行同構的體現轉換成本極低。第三步確認下單 清空購物車。POST /order/new攜帶confirmedtrue時調用OrderService.insertOrder()service/OrderService.java這是一個Transactional事務方法先用Sequence取訂單號 → 逐行updateInventoryQuantity扣減庫存 → 插入訂單主表、狀態表和全部行項目。任何一步失敗整體回滾庫存扣減與訂單落庫強一致。成功后執行關鍵一行session.setAttribute(cart, new Cart());用一個新的空 Cart 覆蓋 Session 里的舊值——既避免殘留舊引用又與懶初始化模式無縫銜接下一輪購物從零開始。六、單元測試與設計總結src/test/java/org/mybatis/jpetstore/domain/CartTest.java用 JUnit 5 把域模型測得很干凈加購合并數量、庫存標記、按 ID 刪除含刪除不存在的商品、連續增量、直接設量、空車與多商品的小計計算——基本覆蓋了Cart的每個公開方法。建議新手在理解本文后運行一遍這些測試對照斷言看輸出是學習域模型最快的方式。最后提煉 4 條可直接復用的設計經驗雙數據結構Map 管快速查找、List 管有序展示寫操作保持兩邊同步懶初始化 SessiongetAttribute為 null 才setAttribute按需創建金額必用 BigDecimal且小計在模型內自動重算防止視圖層算錯賬事務邊界放在訂單服務購物車階段不碰數據庫下單瞬間才在Transactional里扣庫存、寫訂單最后重置購物車——讀放大到內存、寫集中到事務。把這套模式吃透再去理解 Redis 購物車、分布式鎖等進階方案就有扎實的對照基礎了 【免費下載鏈接】jpetstore-6A web application built on top of MyBatis 3, Spring 3 and Stripes項目地址: https://gitcode.com/gh_mirrors/jp/jpetstore-6創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考