如何使用有状态会话 Bean 使数据跨页面持久化?

How to make data persistent across pages using a Stateful Session Bean?

我正在使用 JEE 开发购物车。我的 Web 应用程序由一个有状态会话 bean 和两个具有相应 JSP 页面的 servlet 组成。一个 servlet 允许将新项目添加到购物车,另一个允许列出购物车的内容。

有状态会话 bean 应该跨页面保存购物车的状态,但购物车总是显示为空。你能帮我找出我的代码有什么问题并解释为什么当前的实现不够吗?

将新商品添加到购物车的 Servlet:

package my.servlet;

import my.entity.BookEntity;
import my.entity.ItemEntity;
import my.session.BookBean;
import my.session.CartBean;

import javax.ejb.EJB;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
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;

@WebServlet("/item")
public class ItemServlet extends HttpServlet {

  @EJB
  private BookBean bookBean;

  @EJB
  private CartBean cartBean;

  @PersistenceContext(unitName = "book-pu")
  private EntityManager bookManager;

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    ServletContext ctx = getServletContext();
    long book = Long.parseLong(request.getParameter("book"), 10);
    RequestDispatcher dispatcher = ctx.getRequestDispatcher("/jsp/item.jsp");
    request.setAttribute("book", bookManager.find(BookEntity.class, book));
    request.setAttribute("item", null);
    dispatcher.forward(request, response);
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    ServletContext ctx = getServletContext();
    int qty = Integer.parseInt(request.getParameter("qty"), 10);
    long book = Long.parseLong(request.getParameter("book"), 10);
    RequestDispatcher dispatcher = ctx.getRequestDispatcher("/jsp/item.jsp");
    ItemEntity item = new ItemEntity(bookManager.find(BookEntity.class, book), qty);
    request.setAttribute("book", bookManager.find(BookEntity.class, book));
    request.setAttribute("item", item);
    cartBean.addItem(item);
    dispatcher.forward(request, response);
  }
}

用于列出购物车内容的 Servlet:

package my.servlet;

import my.session.CartBean;

import javax.ejb.EJB;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
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;

@WebServlet("/cart")
public class CartServlet extends HttpServlet {

  @EJB
  private CartBean cartBean;

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
  {
    ServletContext ctx = getServletContext();
    RequestDispatcher dispatcher = ctx.getRequestDispatcher("/jsp/cart.jsp");
    request.setAttribute("items", cartBean.getAllItems());
    dispatcher.forward(request, response);
  }
}

有状态会话 Bean:

package my.session;

import java.util.ArrayList;
import java.util.Collection;

import javax.annotation.PostConstruct;
import javax.ejb.LocalBean;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.ejb.StatefulTimeout;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.concurrent.TimeUnit;
import my.entity.ItemEntity;

@LocalBean
@Stateful
@StatefulTimeout(unit = TimeUnit.MINUTES, value = 30)
public class CartBean implements CartItf {

  private Collection<ItemEntity> items;

  @PersistenceContext(unitName = "book-pu")
  private EntityManager itemManager;

  @PostConstruct
  public void initialize() {
    items = new ArrayList<ItemEntity>();
  }

  @Override
  public void addItem(ItemEntity item)
  {
    items.add(item);
  }

  @Override
  public Collection<ItemEntity> getAllItems()
  {
    return items;
  }

  @Remove
  @Override
  public void confirmOrder()
  {
    for (ItemEntity item : items) {
      itemManager.persist(item);
    }
  }
}

您需要确保每次(每个 servlet)重用相同的有状态实例而不是新实例。您有多种选择:

  1. 添加@SessionScoped 并使用@Inject 代替@EJB
  2. 以编程方式查找 bean 实例 (new InitialContext()) 并将其放入 HttpSession