杰瑞科技汇

Java购物车如何用Session实现数据存储?

这是一个非常经典且重要的应用场景,适合理解 Session 的工作原理。

Java购物车如何用Session实现数据存储?-图1
(图片来源网络,侵删)

核心思想

  1. 会话跟踪:HTTP 协议是无状态的,服务器无法区分不同的用户请求是否来自同一个人。HttpSession 是服务器用来跟踪同一个用户在多次请求之间状态的一种机制,当用户第一次访问网站时,服务器会创建一个唯一的 Session ID,并通过 Cookie 将其发送给浏览器,之后浏览器每次请求都会带上这个 Cookie,服务器就能识别出是哪个用户的会话,从而从服务器内存中取出对应的 Session 对象。

  2. 购物车状态:购物车(商品列表、数量、总价等)是典型的“会话级”数据,它只对当前登录的(或匿名的)用户有效,一旦用户关闭浏览器或会话超时,购物车就应该清空,这完美地契合了 HttpSession 的生命周期。

  3. 存储位置:购物车对象本身会作为 HttpSession 的一个属性被存储在服务器内存中,我们只需要在 Session 中用一个唯一的键("cart")来引用这个购物车对象即可。


实现步骤

我们将分步实现一个基于 Session 的购物车。

Java购物车如何用Session实现数据存储?-图2
(图片来源网络,侵删)

步骤 1:准备数据模型 - Product (商品) 和 CartItem (购物车项)

我们需要定义商品和购物车项的 JavaBean。

Product.java

// Product.java
public class Product {
    private int id;
    private String name;
    private double price;
    // 构造器、Getter 和 Setter
    public Product(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
    public int getId() { return id; }
    public String getName() { return name; }
    public double getPrice() { return price; }
    @Override
    public String toString() {
        return "Product{" + "id=" + id + ", name='" + name + '\'' + ", price=" + price + '}';
    }
}

CartItem.java 购物车项不仅仅是商品,它还包含了用户购买的数量。

// CartItem.java
public class CartItem {
    private Product product;
    private int quantity;
    // 构造器、Getter 和 Setter
    public CartItem(Product product, int quantity) {
        this.product = product;
        this.quantity = quantity;
    }
    public Product getProduct() { return product; }
    public int getQuantity() { return quantity; }
    public void setQuantity(int quantity) { this.quantity = quantity; }
    // 计算小计
    public double getSubtotal() {
        return product.getPrice() * quantity;
    }
    @Override
    public String toString() {
        return "CartItem{" + "product=" + product + ", quantity=" + quantity + '}';
    }
}

步骤 2:创建购物车逻辑 - CartManager (购物车管理器)

这是一个核心类,负责购物车的所有业务逻辑,如添加商品、移除商品、清空购物车、计算总价等。

Java购物车如何用Session实现数据存储?-图3
(图片来源网络,侵删)

CartManager.java

// CartManager.java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CartManager {
    // 使用 Map 来存储购物车项,key 是商品ID,方便快速查找和更新
    private Map<Integer, CartItem> items;
    public CartManager() {
        this.items = new HashMap<>();
    }
    // 添加商品到购物车
    public void addItem(Product product) {
        CartItem item = items.get(product.getId());
        if (item == null) {
            // 如果购物车中没有该商品,则新增一项
            items.put(product.getId(), new CartItem(product, 1));
        } else {
            // 如果已存在,则数量加一
            item.setQuantity(item.getQuantity() + 1);
        }
    }
    // 从购物车移除商品
    public void removeItem(int productId) {
        items.remove(productId);
    }
    // 更新购物车中商品的数量
    public void updateQuantity(int productId, int newQuantity) {
        CartItem item = items.get(productId);
        if (item != null) {
            if (newQuantity > 0) {
                item.setQuantity(newQuantity);
            } else {
                // 如果数量小于等于0,则移除该商品
                items.remove(productId);
            }
        }
    }
    // 清空购物车
    public void clearCart() {
        items.clear();
    }
    // 获取购物车中所有商品项
    public List<CartItem> getItems() {
        return new ArrayList<>(items.values());
    }
    // 计算购物车总金额
    public double getTotalPrice() {
        double total = 0.0;
        for (CartItem item : items.values()) {
            total += item.getSubtotal();
        }
        return total;
    }
}

步骤 3:在 Servlet 中使用 HttpSession 管理购物车

我们创建 Servlet 来处理用户请求,如浏览商品、添加商品、查看购物车等。

假设我们有一个商品列表页面 (products.jsp),可以点击“加入购物车”链接。

AddToCartServlet.java 这个 Servlet 处理“加入购物车”的请求。

// AddToCartServlet.java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
@WebServlet("/addToCart")
public class AddToCartServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 获取请求参数(商品ID)
        String productIdStr = req.getParameter("id");
        if (productIdStr == null || productIdStr.isEmpty()) {
            resp.sendRedirect("products.jsp"); // 如果没有ID,重定向回商品页
            return;
        }
        int productId = Integer.parseInt(productIdStr);
        // 2. 模拟从数据库或服务层获取商品信息
        // 在实际项目中,这里应该调用 DAO 或 Service 层
        Product product = findProductById(productId); // 假设这个方法能找到商品
        // 3. 获取或创建 HttpSession
        HttpSession session = req.getSession(true); // session 不存在,则创建一个新的
        // 4. 从 Session 中获取购物车对象
        // 我们使用 "cart" 作为 Session 中存储购物车的键
        CartManager cart = (CartManager) session.getAttribute("cart");
        // 5. 如果购物车不存在,则创建一个新的并放入 Session
        if (cart == null) {
            cart = new CartManager();
            session.setAttribute("cart", cart);
        }
        // 6. 将商品添加到购物车
        if (product != null) {
            cart.addItem(product);
        }
        // 7. 重定向到购物车页面或商品列表页面
        resp.sendRedirect("cart.jsp");
    }
    // 模拟方法:根据ID查找商品
    private Product findProductById(int id) {
        // 这里只是模拟,实际项目中应该从数据库查询
        switch (id) {
            case 1: return new Product(1, "Java编程思想", 108.00);
            case 2: return new Product(2, "深入理解Java虚拟机", 89.00);
            case 3: return new Product(3, "Effective Java", 75.00);
            default: return null;
        }
    }
}

ViewCartServlet.java 这个 Servlet 用于显示购物车内容。

// ViewCartServlet.java
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
@WebServlet("/viewCart")
public class ViewCartServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession(false); // 不自动创建session
        CartManager cart = null;
        if (session != null) {
            cart = (CartManager) session.getAttribute("cart");
        }
        // 如果购物车不存在或为空,设置一个空列表,避免JSP报错
        if (cart == null || cart.getItems().isEmpty()) {
            req.setAttribute("cartItems", new ArrayList<CartItem>());
            req.setAttribute("totalPrice", 0.0);
        } else {
            req.setAttribute("cartItems", cart.getItems());
            req.setAttribute("totalPrice", cart.getTotalPrice());
        }
        // 转发到 JSP 页面进行展示
        req.getRequestDispatcher("cart.jsp").forward(req, resp);
    }
}

步骤 4:创建 JSP 页面进行展示和交互

products.jsp (商品列表页)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>商品列表</title>
</head>
<body>
    <h1>欢迎光临我们的书店</h1>
    <ul>
        <li>
            <a href="addToCart?id=1">Java编程思想 - ¥108.00</a>
        </li>
        <li>
            <a href="addToCart?id=2">深入理解Java虚拟机 - ¥89.00</a>
        </li>
        <li>
            <a href="addToCart?id=3">Effective Java - ¥75.00</a>
        </li>
    </ul>
    <hr>
    <a href="viewCart">查看我的购物车</a>
</body>
</html>

cart.jsp (购物车页面)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.List" %>
<%@ page import="com.example.CartItem" %>
<html>
<head>我的购物车</title>
</head>
<body>
    <h1>我的购物车</h1>
    <table border="1" cellpadding="5" cellspacing="0">
        <tr>
            <th>商品名称</th>
            <th>单价</th>
            <th>数量</th>
            <th>小计</th>
        </tr>
        <%
            List<CartItem> cartItems = (List<CartItem>) request.getAttribute("cartItems");
            if (cartItems == null || cartItems.isEmpty()) {
        %>
                <tr>
                    <td colspan="4">您的购物车是空的!</td>
                </tr>
        <%
            } else {
                for (CartItem item : cartItems) {
        %>
                    <tr>
                        <td><%= item.getProduct().getName() %></td>
                        <td>¥<%= String.format("%.2f", item.getProduct().getPrice()) %></td>
                        <td><%= item.getQuantity() %></td>
                        <td>¥<%= String.format("%.2f", item.getSubtotal()) %></td>
                    </tr>
        <%
                }
            }
        %>
    </table>
    <hr>
    <p style="font-size: 1.2em; font-weight: bold;">
        购物车总金额: ¥<%= String.format("%.2f", request.getAttribute("totalPrice")) %>
    </p>
    <a href="products.jsp">继续购物</a>
</body>
</html>

  1. 用户访问商品列表:用户打开 products.jsp,看到商品列表。
  2. 点击“加入购物车”:用户点击一个商品旁的链接,addToCart?id=1
  3. AddToCartServlet 处理请求
    • 获取商品ID 1
    • 获取或创建 HttpSession 对象。
    • 从 Session 中以 "cart" 为键取出 CartManager 对象,如果第一次购物,Session 中没有 "cart",则创建一个新的 CartManager 实例并存入 Session。
    • 调用 cart.addItem(product) 将商品ID为1的商品加入购物车。
    • 重定向到 cart.jsp
  4. 显示购物车:浏览器请求 cart.jspViewCartServlet (或由 JSP 容器直接处理) 获取 Session 中的 CartManager,计算总价,并将数据存入 request 作用域,然后转发给 cart.jsp
  5. cart.jsp 渲染页面:JSP 通过 EL 表达式或 JSP 脚本读取 request 中的数据,将购物车内容以表格形式展示给用户。

Session 购物车的优缺点

优点

  • 实现简单HttpSession 是 Servlet API 内置的,使用起来非常方便。
  • 数据安全:购物车数据存储在服务器端,客户端无法篡改,保证了数据的安全性。
  • 高性能(读取):由于数据在内存中,读取和更新速度非常快。

缺点

  • 服务器内存压力大:每个活跃用户的 Session 都会占用服务器内存,如果用户量巨大,会消耗大量服务器资源,成为性能瓶颈。
  • 非持久化:如果服务器重启,所有 Session 数据都会丢失,用户的购物车也会被清空,这在生产环境中是不可接受的。
  • 分布式环境问题:在负载均衡或集群环境下,如果用户的请求被分发到不同的服务器,而 Session 数据只存储在其中一台服务器上,就会出现“找不到购物车”的问题(需要配置 Session 共享,如使用 Redis 或数据库)。

改进方案:结合数据库或缓存

对于生产环境,通常会采用更健壮的方案:

  1. 使用 Redis 作为 Session 存储:将 Session 数据从服务器内存转移到 Redis 中,这解决了服务器重启数据丢失和分布式环境 Session 共享的问题,Redis 本身是内存数据库,读写速度依然很快。
  2. 购物车数据存入数据库:将购物车信息作为用户数据的一部分存入数据库,这种方式数据最可靠,但读写性能不如内存,通常可以结合缓存(如 Redis)来提升性能,即“先读缓存,缓存没有再读数据库”。

HttpSession 购物车是学习和理解 Web 会话管理的绝佳入门方式,在实际项目中,需要根据业务规模和架构选择更合适的存储方案。

分享:
扫描分享到社交APP
上一篇
下一篇