在本文中,我们将了解如何在简单的Web应用程序中使用状态会话Bean来跟踪客户端会话中的状态。
1.简介
有状态会话Bean通常保存有关特定客户端会话的信息,并在整个会话中保留该信息(与无状态会话Bean相对)。 有状态EJB实例仅与一个客户端耦合。 当然,一个客户端可以拥有许多EJB实例。
在此示例中,我们将创建一个简单的Shopping Cart应用程序。 会话bean将保存产品列表。 随着客户将更多产品添加到购物车中,此列表将相应增加。 最终,客户将能够签出他的订单,并且上述列表中的产品将保存在MySQL数据库中。
为了实现上述功能,我们将创建一个EAR项目和一个EJB项目来托管我们的会话Bean,并创建一个动态Web应用程序来托管一个Servlet,以测试上述行为。 我们将使用Eclipse Java EE IDE 4,3 Kepler和Glassfish 4.0作为容器。 另外,我们将使用标准的JPA 2.o将我们的产品保存在在localhost上运行的MySQL 5.6.14数据库中。 这是有关如何将MySQL与Glassfish集成的指南。
2.创建一个新的企业应用程序项目
创建一个名为StatefulEJBEAR
的新企业应用程序项目。 在Eclipse IDE中,选择File-> New-> Enterprise Application Project,填写表单,然后单击Finish:
3.创建一个新的EJB Projet
创建一个名为StatefulSessionBeansEJB
的新EJB项目。 我们将基于此创建会话bean。 转到文件->新建-> EJB项目并填写表单。 请小心选择“添加EAR项目”,然后选择“ StatefulEJBEAR
”作为EAR项目名称:
4.创建一个有状态会话Bean
在项目资源管理器中打开StatefulSessionBeansEJB
项目,并在文件夹ejbModule
创建一个名为com.javacodegeeks.enterprise.ejb
的新源包。 在该程序包中,创建一个新的Interface
,它将是EJB的本地视图:
Cart.java:
package com.javacodegeeks.enterprise.ejb;import javax.ejb.Local;import com.javacodegeeks.enterprise.product.Product;@Local
public interface Cart {void addProductToCart(Product product);void checkOut();}
如您所见,我们声明了两种方法,一种方法是将产品添加到购物车,另一种方法是检查订单。
这是会话Bean:
CartBean.java:
package com.javacodegeeks.enterprise.ejb;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;import javax.annotation.PostConstruct;
import javax.ejb.Stateful;
import javax.ejb.StatefulTimeout;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;import com.javacodegeeks.enterprise.product.Product;@Stateful
@StatefulTimeout(unit = TimeUnit.MINUTES, value = 20)
public class CartBean implements Cart {@PersistenceContext(unitName = "pu", type = PersistenceContextType.EXTENDED)private EntityManager entityManager;private List products;@PostConstructprivate void initializeBean(){products = new ArrayList<>();}@Overridepublic void addProductToCart(Product product) {products.add(product);}@Override@TransactionAttribute(TransactionAttributeType.REQUIRED)public void checkOut() {for(Product product : products){entityManager.persist(product);}products.clear();}
}
如您所见,我们的Session bean实现了我们的Cart
接口,并简单地保存了一个产品列表(稍后将在类Product
上提供更多信息)。
在上面的代码中注意:
- 我们使用
@Stateful
将类注释为有状态会话Bean。 - 我们使用
@StatefulTimeout(unit = TimeUnit.MINUTES, value = 20)
注释声明超时。 此超时表示bean应该存在的时间,因此对于会话有效。 它应该与HTTP会话超时值相对应。 - 我们使用@PersistenceContext注入一个EntityManager来处理产品的持久性。
- 我们在
private void initializeBean()
方法上使用@PostConstruct
批注。 这将指示EJB容器在bean初始化时执行该方法。 您可以将其视为构造函数。 - 我们在
public void checkOut()
方法上使用@TransactionAttribute(TransactionAttributeType.REQUIRED)
批注。 需要使用此注释来表示容器将要在事务上下文中调用业务方法。 您可以按照这种方法查看列表中的产品,并将它们保留在数据库中。
5.产品实体类
这是代表我们购物车应用程序中简单产品的对象。 它由一个ID和一个类型组成。 就像我们说过的,当结帐时,我们希望购物车上的产品能够保存在数据库中。 我们使用JPA 2.0批注将Product
类映射到MySQL表。 对于此示例,我创建了一个简单的数据库,名为shop
和表,其表名为product
,该脚本是使用以下脚本创建的:
MySQL产品表创建脚本:
CREATE TABLE `product` (`ID` int(11) NOT NULL AUTO_INCREMENT,`TYPE` varchar(256) COLLATE utf8_unicode_ci NOT NULL,PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
这是表格:
让我们看一下Product
类的代码:
产品.java:
package com.javacodegeeks.enterprise.product;import java.io.Serializable;import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;@Entity
@Table(name = "PRODUCT", catalog = "shop")
public class Product implements Serializable {private static final long serialVersionUID = 1L;@Id@GeneratedValue(strategy = GenerationType.IDENTITY)@Column(name = "ID", nullable = false)private int id;@Column(name = "TYPE", nullable = false)private String type;public int getId() {return id;}public String getType() {return type;}public void setType(String description) {this.type = description;}}
以上注释不言自明。 非常简单地,我们使用:
- @Entity将类声明为Entity。
-
@Table(name = "PRODUCT", catalog = "shop")
显示该类将映射到名为shop
的数据库中名为product
的表。 -
@Id
id
,@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
声明类Product
的字段id
将是相应数据库表的主键。 -
@Column
将类字段映射到表product
的数据库列。
最后,为了使持久性起作用,我们需要在ejbModule/META-INF
文件夹内创建一个persistence.xml
文件。 该文件如下所示:
application.xml:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"version="2.0"><persistence-unit name="pu" transaction-type="JTA"><jta-data-source>jdbc/MySQLDataSource</jta-data-source> <class>com.javacodegeeks.enterprise.product.Product</class></persistence-unit></persistence>
确保使用此队列已将MySQL与Glassfish正确集成。 有关persistence.xml
文件的更多信息,请参见此Oracle指南 。
因此, StatefulBeansEJB
的最终项目结构为:
6.创建一个新的动态Web项目
转到文件->新建->动态Web项目。 填写表单,确保您选中“将项目添加到EAR”,并将StatefulEJBEAR用作“ EAR项目名称”:
单击“完成”后,转到项目资源管理器,然后右键单击项目StatefulSessionBeansTest
然后转到“属性”->“部署程序集”->“添加”->“项目”->“ StatefulEJB”:
7.创建一个新的Servlet
转到StatefulSessionBeansTest
Web项目并创建一个名为ShoppingCartServlet
的新Servlet:
因此,这将是Web项目的最终结构:
这是Servlet的代码:
ShoppingCartServlet.java:
package com.javacodegeeks.enterprise.servlet;import java.io.IOException;import javax.naming.InitialContext;
import javax.naming.NamingException;
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 com.javacodegeeks.enterprise.ejb.Cart;
import com.javacodegeeks.enterprise.product.Product;@WebServlet("/ShoppingCartServlet")
public class ShoppingCartServlet extends HttpServlet {private static final long serialVersionUID = 1L;private static final String CART_SESSION_KEY = "shoppingCart";public ShoppingCartServlet() {super();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {System.out.println("Hello from servlet");Cart cartBean = (Cart) request.getSession().getAttribute(CART_SESSION_KEY);if(cartBean == null){// EJB is not yet in the HTTP session// This means that the client just sent his first request// We obtain a CartBean instance and add it to the session object.try {InitialContext ic = new InitialContext();cartBean = (Cart) ic.lookup("java:global/StatefulEJBEAR/StatefulSessionBeansEJB/CartBean!"+ "com.javacodegeeks.enterprise.ejb.Cart");request.getSession().setAttribute(CART_SESSION_KEY, cartBean);System.out.println("shoppingCart created");} catch (NamingException e) {throw new ServletException(e);}}String productName = request.getParameter("product");if(productName != null && productName.length() > 0){Product product = new Product();product.setType(productName);cartBean.addProductToCart(product);System.out.println("product "+productName+" added");}String checkout = request.getParameter("checkout");if(checkout != null && checkout.equalsIgnoreCase("yes")){// Request instructs to complete the purchasecartBean.checkOut();System.out.println("Shopping cart checked out ");}}}
在上面的Serlvet中,当用户第一次发送GET请求时,将从容器中获取一个新的CartBean
实例并将其添加到会话中。 然后,解析product
查询参数,如果它不为null,则将使用productName
type
的新Product
并将其添加到会话bean中的产品列表中。
然后,分析checkout
查询参数,如果将其评估为'yes'
,则会话Bean中的产品将被保留。
提示: 如果您在确定EJB PassivationObject的可移植JNDI名称时遇到麻烦,请在部署项目时查看Glassfish的日志或输出,您会发现类似以下内容的行: 2013-12-13T18:22:28.598 + 0200 | INFO :EJB5181:EJB PassivationObject的便携式JNDI名称:(java:global / StatefulBeans / StatefulEJB / PassivationObject,java:global / StatefulBeans / StatefulEJB / PassivationObject!com.javacodegeeks.enterprise.ejb.Passivation)
8.测试
现在,我们仅要将Dynamic Web Application部署到Glassfish,并在购物车中添加一些产品。 然后,我们将要求结帐该订单。
假设我们要添加一些产品,我们可以发出以下请求:
http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?product=ram
http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?product=mouse
http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?product=ssd
发布这些请求时,这是控制台的输出:
2014-01-07T22:02:07.622+0200|INFO: Hello from servlet
2014-01-07T22:02:07.684+0200|INFO: shoppingCart created
2014-01-07T22:02:07.687+0200|INFO: product ram added
2014-01-07T22:02:12.236+0200|INFO: Hello from servlet
2014-01-07T22:02:12.237+0200|INFO: product mouse added
2014-01-07T22:02:24.851+0200|INFO: Hello from servlet
2014-01-07T22:02:24.851+0200|INFO: product ssd added
现在要签出订单,您可以发出:
http://localhost:8080/StatefulSessionBeansTest/ShoppingCartServlet?checkout=yes
这是控制台的输出:
2014-01-07T22:19:46.444+0200|INFO: Hello from servlet
2014-01-07T22:19:46.537+0200|INFO: Shopping cart checked out
在这里您可以看到数据库中的产品:
下载Eclipse项目
这是Java EE状态会话Bean(EJB)上的示例。 这是此示例的Eclipse项目: StatefulEJBS.zip
翻译自: https://www.javacodegeeks.com/2013/08/java-ee-stateful-session-bean-ejb-example.html