Sun ONE logo上一部分 | 目录 | 索引 | 下一部分
Sun ONE Application Server 7 Enterprise Java Bean 技术开发者指南



使用实体 Bean

本节讲述实体 Bean,并解释在 Sun ONE Application Server 7 中创建实体 Bean 的要求。 



注意

如果不熟悉实体 Bean 或 EJB 技术,请参阅 Java 软件教程:

http://java.sun.com/j2ee/docs.html

关于实体 Bean 的详细信息,请参阅 Enterprise JavaBeans Specification 2.0 的第 9、10、12、13 和 14 章。

关于 Sun ONE 应用服务器的概述资料,请参阅“Sun ONE 应用服务器 Enterprise JavaBean 技术介绍”Sun ONE 应用服务器产品介绍



本节介绍以下主题

关于实体 Bean


实体 Bean 实施基本数据库中存储的实体的对象视图,或者实施现有企业应用程序(例如,主机程序或 ERP 应用程序)实施的实体的对象视图。客户、订单和产品就是其中一些示例。在实体 Bean 实例和基本数据库传输实体状态的数据访问协议称为对象持久性

本节讨论以下主题:

实体 Bean 特征

实体 Bean 在许多方面不同于会话 Bean。实体 Bean 是持久的,可以同时由多个客户端访问,具有主键,而且可以参加与其他实体 Bean 的关系。

实体 Bean 具有以下特征:

采用实体 Bean 的一个良好情形包括与数据库、文档和其他业务对象进行封装严密的事务性持久交互。

容器

实体 Bean 依靠 Enterprise Bean 管理安全性、并发性、事务处理以及实体 Bean 管理的实体对象的其他容器特有服务。多个客户端可以并发访问一个实体对象,而容器可以通过事务处理透明地处理并发访问。

每个实体都有一个唯一对象标识符。例如,客户实体 Bean 可能按客户编号识别。此唯一标识符或主键使客户端能够查找某个实体 Bean。

与会话 Bean 一样,实体 Bean 可以通过使用部署描述符设置其事务处理属性的方法内的 JDBC 调用访问数据库。容器支持下节讲述的 Bean 管理和容器管理持久性。

持久性

由于实体 Bean 的状态存储在一个有点可持续的存储区中,因而实体 Bean 是持久的。持久性指实体 Bean 的状态存在时间超过应用程序或服务器进程的寿命。

实体 Bean 的持久性可由 Bean 明确执行,并由 Bean 开发者进行编程。这称为 Bean 管理持久性 (BMP)。

也可以利用 Sun ONE 应用服务器和 Enterprise Bean 的持久性管理把持久性管理委托给容器。此方法称为容器管理持久性 (CMP)。在 CMP 机制中,需要使用与 Sun ONE 应用服务器集成的持久性管理器来确保可靠的持久性。有关容器管理持久性的其他信息,请参阅“将容器管理持久性用于实体 Bean”

下图显示持久性在 Sun ONE 应用服务器环境中的工作方式。


Figure shows persistence flow for entity beans, including persistence manager, transaction manager, BMP/CMP beans, and database.

有关如何为您的应用程序选择最适当持久性方法的准则,请参阅“确定实体 Bean 使用”

本节介绍以下主题:

Bean 管理持久性

Bean 管理持久性中,Bean 负责自己的持久性。您所编写的实体 Bean 代码包含访问数据库的调用。

您通过直接在 Bean 类方法中提供数据库访问调用 - 通过 JDBC 和 SQL,对 Bean 管理实体 Bean 进行编码。数据库访问调用必须在 ejbCreateejbRemoveejbFindXXXejbLoad 和 ejbStore 方法中。此方法的优点是可以毫不费尽地把这些 Bean 部署到应用服务器。缺点是数据库访问非常昂贵,而且在某些情况下,应用服务器能够比应用编程人员更好地优化数据库访问。另外,Bean 管理持久性需要开发人员编写 JDBC 代码。

有关使用 JDBC 操作数据的详细信息,请参阅 Sun ONE 应用服务器J2EE 特性和服务开发者指南

容器管理持久性

容器管理持久性方面,Enterprise Bean 容器通过利用持久性管理器进行交互,处理实体 Bean 所需的一切数据库访问。Bean 代码不包含任何数据库访问 (JCBC) 调用。因此,Bean 代码没有捆绑到特定持久性存储机制(数据库)。由于具有此灵活性,即使在不同数据库上重新部署同一实体 Bean,也不需要修改 Bean 代码。简言之,实体 Bean 更具可移植性。

Bean 开发人员提供抽象 Bean 类。一般来说,容器管理持久性运行时生成知道如何(在 ejbLoadejbStore 方法中)加载和保存 Bean 状态的具体实施类。

要生成数据访问调用,容器需要您在实体 Bean 的抽象架构中提供的信息。有关抽象架构的其他信息,请参阅“抽象架构”

只读 Bean

只读 bean 是一个从不会被 EJB 客户端修改的实体 Bean。只读 Bean 表示的数据可能由其他 Enterprise Bean 或通过其他手段(如直接数据库更新)从外部进行更新。



注意

就 Sun ONE 应用服务器的这个版本而言,只能把使用 Bean 管理持久性的实体 Bean 指定为只读。



只读 Bean 最适合于基本数据从不变化或极少变化的情形。有关创建只读 Bean 的说明,请参阅“使用只读 Bean”

开发实体 Bean


创建实体 Bean 时,必须提供许多类文件。下列主题中将讨论所需执行的任务:

确定实体 Bean 使用

如果 Bean 表示一个业务实体,而不是一个过程,而且 Bean 的状态必须是持久的(如果关闭服务器,数据库中仍然存在 Bean 的状态),很可能就应使用实体 Bean。

与会话 Bean 不同的是,实体 Bean 实例可同时由多个客户端进行访问。容器负责同步使用事务处理的实例状态。由于此责任是委托给容器执行的,因而您不需要考虑来自多个事务的并发访问方法。

您所选择的持久性方法也有影响:

Bean 开发人员的责任

本节讲述您需要做什么工作才能确保可以把具有 Bean 管理持久性的实体 Bean 部署在 Sun One 应用服务器上。

实体 Bean 开发人员负责提供以下类文件:

定义主键类

EJB 体系架构使主键类可以成为属于 RMI-IIOP 中合法“值类型”的任何类。此类必须能够适当实施 hashCode 和相等(其他对象)方法。 主键类可以是实体 Bean 所特有,即每个实体 Bean 类可以为其主键定义一个不同的类,但是,多个实体 Bean 可以使用同一主键类。

您必须在部署描述符中指定一个主键类。

定义远程接口

本节讨论以下主题:

创建远程home接口

作为一个 Bean 开发者,您必须提供 Bean 的远程home接口(如果适用的话)。home接口定义使一个访问应用程序的客户端能够创建、查找和删除实体对象方法。您创建的远程home接口必须符合以下要求:

远程创建方法

远程查找方法

findByPrimaryKey 方法

远程删除方法

所有home接口都自动(通过扩展 javax.ejb.EJBHome)定义两个 remove 方法,用来毁坏不再需要的 Enterprise Bean:

public void remove(java.lang.Object primaryKey)
throws java.rmi.RemoteException, RemoveException

public void remove(Handle handle)
throws java.rmi.RemoteException, RemoveException



注意

不要覆盖这些 remove 方法。



远程home接口示例


import javax.ejb.*;
import java.rmi.*;

public interface MyEntityBeanLocalHome
   extends EJBHome
{
   /**
      * Create an Employee
      * @param empName Employee name
      * @exception CreateException If the employee cannot be
         created
         * @return The remote interface of the bean
      */
   public MyEntity create(String empName)
      throws CreateException;
   /**
      * Find an Employee
      * @param empName Employee name
      * @exception FinderException if the empName is not found
      * @return The remote interface of the bean
      */
   public MyEntity findByPrimaryKey(String empName)
      throws FinderException;
}

定义本地接口

要构建一个允许本地方法的 Enterprise Bean,您必须对本地接口和本地home接口进行编码。本地接口定义 Bean 的业务方法;本地home接口定义其生命周期(创建/删除)和查找程序方法。

本节介绍以下主题:

创建本地home接口

home接口定义使使用应用程序的客户端能够创建和删除实体 Bean 的方法。一个 Bean 的本地home接口定义使本地客户端可以创建、查找和删除 EJB 对象以及不是 Bean 实例特有的主业务方法(会话 Bean 不具有查找程序和主业务方法)。本地home接口由您定义,并由容器进行实施。客户端使用 JNDI 查找一个 Bean 的 home。 

本地home接口使客户端可以:

本地home接口始终扩展 javax.ejb.EJBLocalHome。例如:

   import javax.ejb.*;
   public interface MyEntityLocalBeanHome extends EJBLocalHome {
      MyEntityLocalBean create() throws CreateException;
   }

创建本地接口

如果某个实体 Bean 是一种容器管理关系的目标,该实体 Bean 必须具有本地接口。关系的方向确定某个 Bean 是否是一个目标。由于实体 Bean 需要本地访问,因此,参加容器管理关系的实体 Bean 必须驻留在同一 EJB JAR 文件中。此本地性的主要优点是提高了性能 - 本地调用比远程调用快。

由于本地接口遵循 pass by reference 语义,您必须明白可以共享通过本地接口传递的对象。尤其是,注意不要把一个 Enterprise Bean 的状态指定为另一个 Enterprise Bean 的状态。您还必须小心确定在本地接口之间传递哪些对象,尤其是在交易或安全内容发生变化的情况下。 

(本地)创建方法

(本地)查找方法

findByPrimaryKey 方法

(本地)home方法

创建远程接口

除了您在远程接口中定义的业务方法之外,EJBObject 接口定义使您能够执行以下操作的多种抽象方法:

有关这些内置方法以及如何使用这些方法的详细信息,请参阅 Enterprise JavaBeans Specification 2.0。



注意

Enterprise JavaBeans Specification 2.0 允许 Bean 类实施远程接口的方法,但是建议不要这样做,以免无意中通过此方法将一个直接引用传递给客户端,因而破坏  Enterprise JavaBeans Specification 2.0 所要求的客户端-容器-EJB 协议。



远程接口示例

以下片段是一个远程接口示例:

import javax.ejb.*;
import java.rmi.*;

public interface MyEntity
   extends EJBObject
   {
   public String getAddress() throws RemoteException;
   public void setAddress(String addr) throws RemoteException;
}

创建 Bean 类定义(适用于 Bean 管理持久性)

对于使用 Bean 管理持久性的实体 Bean,必须把 Bean 类定义为 public,而且不能是 abstract。Bean 类必须实施 javax.ejb.EntityBean 接口。例如:

import java.rmi.*;
import java.util.*;
import javax.ejb.*;
public class MyEntityBean implements EntityBean {
   //    Entity Bean implementation. These methods must always be
   included.
   public void ejbActivate() {
   }
   public void ejbLoad() {
   }
   public void ejbPassivate() {
   }
   public void ejbRemove() {
   }
   public void ejbStore() t{
   }
   public void setEntityContext(EntityContext ctx) {
   }
   public void unsetEntityContext() {
   }
   // other code omitted here....
   }

除了这些方法之外,实体 Bean 类还必须定义一个或多个 ejbCreate 方法和 ejbFindByPrimaryKey 查找程序方法。实体 Bean 类可以随意为每个 ejbCreate 方法定义一个 ejbPostCreate。实体 Bean 类可以提供开发人员定义的额外的查找程序方法,这些方法形式为 ejbFindXXX,其中 XXX 表示一个唯一的方法名称扩展(例如,ejbFindApplesAndOranges),该扩展与任何其他方法名称不相重复。

实体 Bean 一般实施一个或多个业务方法。这些方法通常对于每个 Bean 都是唯一的,并且代表其特定功能。业务方法名称可以是任何事情,但不得与 EJB 体系架构中使用的方法名称相冲突。必须将业务方法声明为 public。方法参数和返回值类型必须是合法 Java RMI。throws 子句可以定义应用程序特有的异常,并且可以包括 java.rmi.RemoteException

有两种在实体 Bean 中实施的业务方法类型:

以下各节介绍实体 Bean 类定义中的各种方法:

使用 ejbCreate

实体 Bean 必须实施一个或多个 ejbCreate 方法。针对允许客户端调用 Bean 的每种方式,都必须有一个方法。例如:

public String ejbCreate(String orderId, String customerId,
   String status, double totalPrice)
   throws CreateException {
      try {
      InitialContext ic = new InitialContext();
      DataSource ds = (DataSource) ic.lookup(dbName);
      con = ds.getConnection();
      String insertStatement =
         "insert into orders values ( ? , ? , ? , ? )";
      PreparedStatement prepStmt =
         con.prepareStatement(insertStatement);
      prepStmt.setString(1, orderId);
      prepStmt.setString(2, customerId);
      prepStmt.setDouble(3, totalPrice);
      prepStmt.setString(4, status);
      prepStmt.executeUpdate();
      prepStmt.close();
   } catch (Exception ex) {
      throw new CreateException("ejbCreate: "
         +ex.getMessage());
   }
}

public String ejbPostCreate(String orderId, String customerId,String
status, double totalPrice)
   throws CreateException

{
......
......
}

必须将每个 ejbCreate 方法声明为 public, 每个 ejbCreate 方法必须返回主键类型,而且必须命名为 ejbCreate。返回类型可以是出于键的目的转换为一个数字的任何合法 Java RMI 类型。所有参数都必须是合法 Java RMI 类型。throws 子句可以定义应用程序特有的异常,并且可以包括 java.ejb.CreateException

这是用以建立关系的方法。对于每个 ejbCreate 方法,实体 Bean 类可以定义一个相应 ejbPostCreate 方法来处理立即跟踪创建的实体服务。必须将每个 ejbPostCreate 方法声明为 public,每个 ejbPostCreate 方法必须返回空白,并且命名为 ejbPostCreate。方法参数(如果有的话)必须在数目和参数类型方面匹配其相应的 ejbCreate 方法。throws 子句可以定义应用程序特有的异常,而且可以包括 java.ejb.CreateException。

使用 ejbActivate 和 ejbPassivate

当一个服务器应用程序需要每个实体 Bean 实例时,该 Bean 的容器调用 ejbActivate 准备好一个要使用的 Bean 实例。同样地,如果不再需要某个实例,该 Bean 的容器就调用 ejbPassivate,取消该 Bean 与应用程序之间的关联。

如果在首次将一个 Bean 准备好供某个应用程序使用时或不再需要某个 Bean 时,需要执行特定应用程序任务,您应在 ejbActivateejbPassivate 方法内编写这些操作的程序。例如,您可以在 ejbPassivate 期间发布对数据库和后台资源的应用,以及在 ejbActivate 期间重新获得它们。

使用 ejbLoad 和 ejbStore

实体 Bean 可以与容器协作,把 Bean 状态信息存储在一个数据库中,以便于执行同步操作。如果是 Bean 管理持久性,您负责对 ejbLoadejbStore 进行编码。容器通过在一项事务开始时调用 ejbLoad 以及在事务成功完成时调用 ejbStore,确保 Bean 的状态与数据库同步。

使用您的 ejbStore 实施将数据信息存储在数据库中,并使用您的 ejbLoad 实施从数据库中检索状态信息。

以下示例显示存储和检索活动数据的 ejbLoadejbStore 方法定义。

public void ejbLoad()
      throws java.rmi.RemoteException
{
   String itemId;
   javax.sql.Connection dc = null;
   java.sql.Statement stmt = null;
   java.sql.ResultSet rs = null;

   itemId = (String) m_ctx.getPrimaryKey();

   System.out.println("myBean: Loading state for item " + itemId);

   String query =
      "SELECT s.totalSold, s.quantity " +
      " FROM Item s " +
      " WHERE s.item_id = " + itemId;

   dc = new DatabaseConnection();
   dc.createConnection(DatabaseConnection.GLOBALTX);
   stmt = dc.createStatement();
   rs = stmt.executeQuery(query);

   if (rs != null) {
      rs.next();
      m_totalSold = rs.getInt(1);
      m_quantity = rs.getInt(2);
   }
}

public void ejbStore()
      throws java.rmi.RemoteException
{
   String itemId;
   itemId = (String) m_ctx.getPrimaryKey();
   DatabaseConnection dc = null;
   java.sql.Statement stmt1 = null;
   java.sql.Statement stmt2 = null;

   System.out.println("myBean: Saving state for item = " + itemId);

   String upd1 =
      "UPDATE Item " +
      " SET quantity = " + m_quantity +
      " WHERE item_id = " + itemId;

   String upd2 =
      "UPDATE Item " +
      " SET totalSold = " + m_totalSold +
      " WHERE item_id = " + itemId;

   dc = new DatabaseConnection();
   dc.createConnection(DatabaseConnection.GLOBALTX);
   stmt1 = dc.createStatement();
   stmt1.executeUpdate(upd1);
   stmt1.close();
   stmt2 = dc.createStatement();

   stmt2.executeUpdate(upd2);
   stmt2.close();
}

有关访问与其他 Bean 并发的事务的 Bean 隔离级别的详细信息,请参阅

使用 setEntityContext 和 unsetEntityContext

容器在其创建一个实体 Bean 实例(该实例为 Bean 提供一个连接容器的接口)之后,调用 setEntityContext。实施此方法存储容器传递的实体上下文。随后,您可以使用此引用获取该实例的主键,等等。

   public void setEntityContext(javax.ejb.EntityContext ctx)
   {
   m_ctx = ctx;
   }

同样地,容器调用 unsetEntityContext 从实例中删除容器引用。这是 Bean 实例成为要删除的对象之前容器调用的最后一个 Bean 类方法。执行此调用之后,Java 垃圾收集机制最终调用实例上的 finalize 将其清除,并加以处置。

   public void unsetEntityContext()
   {
   m_ctx = null;
   }

使用 ejbRemove

客户端可以调用实体 Bean 的home接口或组件接口上的删除方法,从数据库中删除相关记录。容器调用某个实体 Bean 实例上的 ejbRemove 方法,响应实体 Bean 的home接口或组件接口上的客户端调用,或者作为 cascade-delete 操作的结果。

使用查找程序方法

由于实体 Bean 具有持久性、可在客户端之间共享,可拥有不只一个同时实例化的实例,因此,一个实体 Bean 必须实施至少一个 ejbFindByPrimaryKey 方法。这使客户端和容器能够查找特定 Bean 实例。所有实体 Bean 必须提供一个唯一主键,作为一个识别签名。在 Bean 的类中实施 ejbFindByPrimaryKey 方法,使一个 Bean 能够将其主键返回到容器。

下面的示例显示 FindByPrimaryKey 的一个定义:

public String ejbFindByPrimaryKey(String key)
      throws java.rmi.RemoteException,
         javax.ejb.FinderException

在某些情况下,您根据 Enterprise Bean 的作用、根据实例所使用的某些值或根据其他条件,查找特定实体 Bean 实例。这些针对实施的查找程序方法名称的形式为 ejbFindXXX,其中 XXX 表示一个不重复任何其他方法名称(例如,ejbFindApplesAndOranges)的方法名称的唯一扩展。 

必须将查找程序方法声明为 public ,而且其参数以及返回值必须为合法 Java RMI 类型。每个查找程序方法返回类型必须是实体 Bean 的主键类型或具有同一主键类型的对象的集合。如果返回类型为一个集合,返回类型必须是以下两项之一:

查找程序方法的 throws 子句是一个应用程序特有的异常,而且可能包括 java.rmi.RemoteException 和/或 java.ejb.FinderException

使用只读 Bean


只读 Bean 是一种 EJB 客户端从不修改的实体 Bean。只读 Bean 表现的数据可以由其他 Enterprise Bean 从外部进行更新,也可以通过其他手段更新,如直接数据库更新。



注意

对于此版 Sun ONE 应用服务器,只能把使用 Bean 管理持久性的实体 Bean 指定为只读。

只读 Bean 为 Sun ONE 应用服务器所特有,而且不是 Enterprise JavaBeans Specification 2.0 的组成部分。



本节介绍以下主题:

只读 Bean 特征和生命周期

只读 Bean 最适合于基本数据从不变化或极少变化的情形。例如,只读 Bean 可用来表示某个公司的股票价格(外部更新)。在此情况下,使用常规实体 Bean 可能会增加调用 ejbStore 的负担,使用只读 Bean 就可以避免这种情况。

只读 Bean 具有以下特征:

只读 Bean 使用适当查找方法而存在。

只读 Bean 被缓存起来,并具有与实体 Bean 相同的缓存属性。当选择只读 Bean 作为在缓存中腾出空间的牺牲者时,就调用 ejbPassivate,并将 Bean 返回到自由池。在自由池时,Bean 不具有任何标识,并将仅用于作为任何查找程序请求。

只读 Bean 被捆绑到像读写实体 Bean 这样的命名服务,而且客户端可以用查找读写实体 Bean 一样的方法查找只读 Bean。

只读 Bean 良好做法

刷新只读 Bean

刷新只读 Bean 的方法有多种,它们在以下各节中进行讨论:

调用事务方法

调用任何事务方法将会调用 ejbLoad。

定期刷新

通过在 Sun ONE 应用服务器特有的 XML 文件中指定 refresh-period-in-seconds 元素,可以定期刷新只读 Bean。

以编程方式刷新

一般情况下,更新只读 Bean 缓存的任何数据的 Bean 需要通知只读 Bean 刷新其状态。您可以使用 ReadOnlyBeanNotifier 来强制刷新只读 Bean。为此,在 ReadOnlyBeanNotifier Bean 上调用以下方法:

   public interface ReadOnlyBeanNotifier
      extends java.rmi.Remote
   {
      refresh(Object PrimaryKey)
         throws RemoteException;
   }

容器提供对 ReadOnlyBeanNotifier 接口的实施。用户可以使用以下代码片段查找 ReadOnlyBeanNotifier

com.sun.ejb.ReadOnlyBeanNotifier notifier = com.sun.ejb.containers.ReadOnlyBeanHelper.getReadOnlyBeanNotifier
   (<ejb-name-of -the-target>);
   notifier.refresh(<PrimaryKey>);

更新只读 Bean 缓存的任何数据的 Bean 需要调用 refresh 方法。下一次调用只读 Bean 的(非事务性)调用时,就会调用 ejbLoad

部署只读 Bean

只读 Bean 是用与其他实体 Bean 相同的方式部署的。但是,在 Sun ONE 应用服务器特有的 XML 文件中的 Bean 的输入中,必须把 is-read-only-bean 元素设置为 True(真)。也就是:

<is-read-only-bean>true</is-read-only-bean>

另外,可以把 refresh-period-in-seconds 元素设置为指定刷新 Bean 的频率的某些值。如果丢失此元素,就假定  600(秒)默认值。 

具有相同事务上下文的所有请求都被路由到同一只读 Bean 实例。部署者可以通过将 allow-concurrent-access 元素设置为真(允许同时方法)或 False(假)(将并发访问序列化为同一只读 Bean),指定是否必须序列化这么多请求。

有关这些元素的详细信息,请参阅 Sun ONE 应用服务器管理员配置文件参考

处理并发访问的同步


作为一名实体 Bean 开发者,您一般不需要考虑从多个事务并发访问一个实体 Bean。在这些情况下,该 Bean 的容器自动提供同步。在 Sun ONE 应用服务器中,容器针对每个同时发生并使用 Bean 的事务,激活一个实体 Bean。

事务同步是由基本数据库在数据库访问调用期间自动执行的。您一般与基本数据库或资源一起执行此同步。一种方法是在 ejbLoad 方法中获得相应数据库锁定,例如通过选择适当隔离级别或通过使用一个用于 update 子句的 select。具体情况取决于所使用的数据库。

有关详细信息,请参阅 Enterprise JavaBeans Specification 2.0,因为该规范与并发访问相关。

上一部分 | 目录 | 索引 | 下一部分
Copyright 2002 Sun Microsystems, Inc. All rights reserved.