杰瑞科技汇

Java Session购物车如何实现数据持久化?

核心思想

Session(会话)是服务器用来跟踪单个用户从进入网站到离开网站期间的所有交互的一种机制,服务器在用户首次访问时创建一个唯一的 Session ID,并将其发送给浏览器(通常通过 Cookie 存储),之后,浏览器每次请求都会携带这个 Session ID,服务器通过 ID 找到对应的 Session 对象,从而实现“用户状态的功能。

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

购物车的核心逻辑就是:将用户选择的商品信息临时存储在 Session 中。 因为 Session 是与用户绑定的,所以每个用户都有自己独立的购物车,互不干扰。


实现步骤

我们将通过一个完整的示例来演示这个过程,这个示例将包含:

  1. 商品列表页面 (index.jsp)
  2. 添加商品到购物车的 Servlet (AddToCartServlet.java)
  3. 购物车页面 (cart.jsp)
  4. 清空购物车的 Servlet (ClearCartServlet.java)

第 1 步:项目结构与依赖

我们使用一个简单的 Maven Web 项目。

项目结构:

Java Session购物车如何实现数据持久化?-图2
(图片来源网络,侵删)
src
└── main
    ├── java
    │   └── com
    │       └── example
    │           └── cart
    │               ├── model
    │               │   └── Product.java
    │               ├── servlet
    │               │   ├── AddToCartServlet.java
    │               │   └── ClearCartServlet.java
    │               └── util
    │                   └── ProductUtil.java
    ├── resources
    └── webapp
        ├── WEB-INF
        │   └── web.xml
        ├── cart.jsp
        └── index.jsp

pom.xml 依赖:

<dependencies>
    <!-- Servlet API -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- JSP API -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.3</version>
        <scope>provided</scope>
    </dependency>
    <!-- JSTL (JSP Standard Tag Library) - 用于在JSP中方便地遍历集合 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
</dependencies>

第 2 步:创建模型

我们需要一个 Product 类来表示商品。

src/main/java/com/example/cart/model/Product.java

package com.example.cart.model;
public class Product {
    private int id;
    private String name;
    private double price;
    public Product(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
}

第 3 步:创建工具类 (模拟数据库)

为了演示,我们创建一个工具类来提供一些模拟的商品数据。

src/main/java/com/example/cart/util/ProductUtil.java

package com.example.cart.util;
import com.example.cart.model.Product;
import java.util.ArrayList;
import java.util.List;
public class ProductUtil {
    public static List<Product> getProducts() {
        List<Product> products = new ArrayList<>();
        products.add(new Product(1, "Apple iPhone 13", 6999.00));
        products.add(new Product(2, "Samsung Galaxy S22", 5999.00));
        products.add(new Product(3, "MacBook Pro 14", 14999.00));
        products.add(new Product(4, "Dell XPS 13", 8999.00));
        return products;
    }
}

第 4 步:创建商品列表页面 (index.jsp)

这个页面展示所有商品,并为每个商品提供一个“加入购物车”的链接。

src/main/webapp/index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>商品列表</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        .product { border: 1px solid #ccc; padding: 15px; margin: 10px; display: inline-block; width: 200px; }
        .product h3 { margin-top: 0; }
        .product p { color: #777; }
        a { text-decoration: none; color: #0066cc; }
        a:hover { text-decoration: underline; }
    </style>
</head>
<body>
    <h1>欢迎来到我们的商店</h1>
    <p>请选择您喜欢的商品:</p>
    <c:forEach var="product" items="${products}">
        <div class="product">
            <h3>${product.name}</h3>
            <p>价格: ¥${product.price}</p>
            <!-- 链接到添加商品的Servlet,并传递商品ID -->
            <a href="add-to-cart?id=${product.id}">加入购物车</a>
        </div>
    </c:forEach>
</body>
</html>

注意: 在 index.jsp 中,我们使用了 ${products},这意味着我们需要在请求 index.jsp 之前,将商品列表放入 request 作用域中,这通常通过一个 IndexServlet 来完成,但为了简化,我们假设 web.xml 中配置了欢迎文件列表,并让 index.jsp 自己处理数据加载(在实际项目中,最好使用一个专门的 Servlet)。

我们修改一下,让 index.jsp 能自己获取数据:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.example.cart.util.ProductUtil, java.util.List" %>
<html>
<!-- ... head ... -->
<body>
    <h1>欢迎来到我们的商店</h1>
    <p>请选择您喜欢的商品:</p>
    <%-- 在JSP中直接调用Java代码来获取数据并放入request --%>
    <%
        List<Product> productList = ProductUtil.getProducts();
        request.setAttribute("products", productList);
    %>
    <c:forEach var="product" items="${products}">
        <!-- ... 商品展示 ... -->
    </c:forEach>
</body>
</html>

第 5 步:创建添加商品的 Servlet (AddToCartServlet.java)

这是购物车的核心,它负责:

  1. 从请求中获取要添加的商品 ID。
  2. ServletContext 中获取所有商品列表(模拟数据库)。
  3. request 中获取 HttpSession 对象。
  4. Session 中获取购物车(一个 Map 集合)。
  5. 如果购物车不存在,则创建一个新的 HashMap 并存入 Session
  6. 将商品添加到购物车 Map 中(商品ID作为Key,商品对象作为Value)。
  7. 重定向回商品列表页面。

src/main/java/com/example/cart/servlet/AddToCartServlet.java

package com.example.cart.servlet;
import com.example.cart.model.Product;
import com.example.cart.util.ProductUtil;
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;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@WebServlet("/add-to-cart")
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("index.jsp");
            return;
        }
        int productId = Integer.parseInt(productIdStr);
        // 2. 从ServletContext中获取所有商品列表 (模拟数据库查询)
        // 在实际应用中,这里应该从数据库或Service层获取
        List<Product> allProducts = (List<Product>) getServletContext().getAttribute("allProducts");
        if (allProducts == null) {
            allProducts = ProductUtil.getProducts();
            getServletContext().setAttribute("allProducts", allProducts);
        }
        // 3. 找到要添加的商品
        Product productToAdd = null;
        for (Product p : allProducts) {
            if (p.getId() == productId) {
                productToAdd = p;
                break;
            }
        }
        if (productToAdd == null) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND, "商品未找到");
            return;
        }
        // 4. 获取或创建Session
        HttpSession session = req.getSession();
        // 5. 获取或创建购物车 (使用Map来存储,Key是商品ID,Value是商品对象)
        Map<Integer, Product> cart = (Map<Integer, Product>) session.getAttribute("cart");
        if (cart == null) {
            cart = new HashMap<>();
            session.setAttribute("cart", cart);
        }
        // 6. 将商品添加到购物车
        // 如果商品已存在,这里会覆盖,如果要实现数量,Value应该是包含数量的对象。
        cart.put(productToAdd.getId(), productToAdd);
        // 7. 重定向回商品列表页面,并显示成功消息
        // 使用重定向可以防止刷新页面时重复提交表单
        resp.sendRedirect("index.jsp?status=added");
    }
}

web.xml 配置 (如果使用注解 @WebServlet 则可以省略):

<servlet>
    <servlet-name>AddToCartServlet</servlet-name>
    <servlet-class>com.example.cart.servlet.AddToCartServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>AddToCartServlet</servlet-name>
    <url-pattern>/add-to-cart</url-pattern>
</servlet-mapping>

第 6 步:创建购物车页面 (cart.jsp)

这个页面展示当前 Session 中的所有商品。

src/main/webapp/cart.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>我的购物车</title>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        table { width: 100%; border-collapse: collapse; }
        th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
        th { background-color: #f2f2f2; }
    </style>
</head>
<body>
    <h1>我的购物车</h1>
    <c:choose>
        <c:when test="${empty sessionScope.cart}">
            <p>您的购物车是空的。</p>
            <a href="index.jsp">去购物</a>
        </c:when>
        <c:otherwise>
            <table>
                <tr>
                    <th>商品名称</th>
                    <th>价格</th>
                </tr>
                <c:forEach var="product" items="${sessionScope.cart.values()}">
                    <tr>
                        <td>${product.name}</td>
                        <td>¥${product.price}</td>
                    </tr>
                </c:forEach>
            </table>
            <br>
            <a href="clear-cart">清空购物车</a>
        </c:otherwise>
    </c:choose>
</body>
</html>

第 7 步:创建清空购物车的 Servlet (ClearCartServlet.java)

这个 Servlet 非常简单,它只需要从 Session 中移除 cart 对象即可。

src/main/java/com/example/cart/servlet/ClearCartServlet.java

package com.example.cart.servlet;
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("/clear-cart")
public class ClearCartServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 1. 获取Session
        HttpSession session = req.getSession(false); // false表示如果不存在则不创建
        // 2. 如果Session存在,则移除购物车
        if (session != null) {
            session.removeAttribute("cart");
        }
        // 3. 重定向回购物车页面
        resp.sendRedirect("cart.jsp");
    }
}

运行与测试

  1. 将项目部署到 Tomcat 或其他 Servlet 容器中。
  2. 启动服务器,访问 http://localhost:8080/your-project-name/
  3. 在商品列表页面,点击某个商品的“加入购物车”链接。
  4. 观察浏览器地址栏会变成 .../add-to-cart?id=1,然后页面会跳转回商品列表。
  5. 再次访问 http://localhost:8080/your-project-name/cart.jsp,你应该能看到刚才添加的商品。
  6. 在另一个浏览器(或隐私模式)中重复步骤 2-5,你会发现两个购物车是独立的,这证明了 Session 的用户隔离性。
  7. 在购物车页面,点击“清空购物车”链接,购物车中的商品将消失。

代码优化与思考

  1. 处理商品数量

    • 当前实现中,重复添加同一个商品会覆盖之前的,如果要支持数量,购物车的 Map 的 Value 应该是一个包含 Productquantity 的对象。
    • Map<Integer, CartItem>, CartItem 类有 Product productint quantity 属性。
    • AddToCartServlet 中,如果商品已存在,只需增加 quantity 即可。
  2. 使用 ServletContext vs HttpSession

    • 我们将 allProducts 放在了 ServletContext 中。ServletContext 是整个应用共享的,适合存放所有用户共享的数据,比如商品列表。
    • HttpSession 是用户私有的,适合存放用户相关的临时数据,比如购物车。
  3. Session 失效

    • Session 会在以下情况失效:服务器关闭、Session 超时(在 web.xml 中配置)、用户主动调用 session.invalidate()
    • 如果用户关闭浏览器,默认情况下 Session ID 丢失,但服务器端的 Session 可能还存在直到超时,如果希望关闭浏览器就清空购物车,可以考虑使用 Cookie 来存储购物车信息,但这会增加复杂性。
  4. 与前端框架结合

    在现代的 Web 开发中(如使用 Vue, React, Angular),购物车的状态通常由前端管理(例如使用 Vuex, Redux),当用户点击“结算”时,前端再将整个购物车数据通过 AJAX 请求发送到后端,这种方式用户体验更好,但 Session 购物车仍然是后端处理业务逻辑和持久化(如生成订单)的基础。

这个完整的示例为你提供了一个使用 Java Session 实现购物车的坚实起点。

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