第5章 实体EJB

  实体EJB用在处理客户端请求大量、并发的情况,它在实现业务逻辑的同时,作为数据库的一个缓冲。在服务量大的情况下,减轻数据库的负担,提高业务处理能力。本章介绍实体EJB、两种持久性管理方法、编程模型和实例开发过程。

5.1 实体EJB编程模型

  1.实体EJB

  实体EJB封装了业务逻辑实现,并且可以供多个客户使用。除了实现业务逻辑外,实体EJB的属性用来代表商业过程中处理的永久性数据。一个简单的实体Bean可以定义成代表数据库表的一个记录,也就是每一个实体对象代表 一条具体的记录。更复杂的实体Bean可以代表数据库表间关联视图。

  2.实体EJB的持久性

  持久性是实体EJB的一个重要概念。

  
        图5-1 EJB客户端视图

  和会话Bean的编程模型一样,实体EJB的客户端视图也是由主接口和远程接口组成。主接口实现类负责实体对象的创建和查询,远程接口实现类负责逻辑方法的调用。

  3.定位一个会话Bean主接口

  客户端使用JNDI定位一个实体Bean主接口。例如,Account实体Bean的主接口可使用以下代码进行查找:
  Context initialContext = new InitialContext();
  AccountHome accountHome = (AccountHome)
  javax.rmi.PortableRemoteObject.narrow(initialContext.lookup("java:comp/env/ejb/accounts"),
  AccountHome.class);
  一个客户端的JNDI命名空间可以配置起来包含网络上在不同机器上的、不同EJB容器中的EJB的主接口。而EJB容器的实际位置对使用企业Bean的客户端来说是透明的。

  4.实体Bean主接口

  部署在容器中实体Bean的主接口的实现是由容器提供的。并且,容器确保客户端能够通过JNDI访问到部署在容顺中的每个实体Bean的主接口。实现实体Bean主接口的对象是EJBHome。
  通过实体Bean主接口,客户端可以进行如下操作:
  ·创建新的实体对象。
  ·查找存在的实体对象。
  ·删除实体对象。
  ·执行主逻辑方法。
  ·获取主接口的句柄。
  主句柄能被序列化,并且能被写入存储设备中。然后,句柄可以在另一个不同的JVM中,从固定存储器中反序列化,获取主接口的引用。
  实体Bean主接口必须扩展javax.ejb.EJB主接口,并遵循Java语言远程接口规范。

  5.create方法

  实体Bean主接口可以定义多个create方法,每种方法都能创建一个实体对象。create方法的参数一般用来初始化实体对象的状态。每种create方法名的前缀是“create”。
  create方法的返回类型是实体bean的远程接口。每个create方法都要定义抛出两个异常java.rmi.RemoteException和javax.ejb.CreateExcention。也可以包含其它的引用级异常。例如:下面主接口定义中演示了三个create方法:

  public Interface AccountHome extends javax.ejb.EJBHome{
   public Account create(String firstName,String lastName,double initialBalance)
    throws RemoteException,CreateException;
   public Account create(String accountNumber,double initialBalance)
    throws RemoteException,CreateException,LowInitialBalanceException;
   public Account createLargeAccount(String firstname,String lastname,
    double initialBalance)
    throws RemoteException,CreateException;
   ...
  }
  下面代码演示客户端程序如何创建一个新的实体对象:
  AccountHome accountHome =...;
  Account account = accountHome.create("John","Smith",500.00);

  6.finder方法

  实体Bean主接口可以定义一个或多个finder方法,每个方法定义一种查询一个实体EJB和多个EJB对象的方法。
  finder方法必须以“find”为前缀,如findLargeAccounts(...),参数由实体Bean的来定位请求的实体对象。finder方法的返回值必须是实体Bean的远程接口,或者是实现实体Bean远程接口对象的集合。定义finder方法的异常有java.rmi.RemoteException和javax.ejb.FinderException。
  每个实体Bean的主接口包含一个findByPrimaryKey(primaryKey)方法。它允许客户端使用主键定位一个实体对象,它的名字总是findByPrimaryKey,只有一个参数,这个参数具有和实体Bean主键相同的类型,返回类型是实体Bean的远程接口。每个实体Bean有惟一的findByPrimaryKey(primaryKey)方法,该方法不能被重载,其实现必须确保实体对象存在。下面代码片断演示findByPrimaryKey方法:
  public Interface AccountHome extends javax.ejb.EJBHome{
   ...
   public Account findByPrimaryKey(String AccountNumber)
   throws RemoteException,FinderException;
  }
  下面代码片断演示客户端如何使用findByPrimaryKey方法:
  AccountHome=...;
  Account account = account 主findByPrimaryKey("100-3450-3333");

  7.remove方法

  javax.ejb.EJBHome接口定义了允许客户端删除实体对象的方法:
  public Interface EJBHome extends Remote{
   void remove(Handle handle) throws RemoteException,RemoveException;
   void remove(Object primaryKey) throws RemoteException,RemoveException;
  }
  实体对象删除后,客户端访问实体对象会产生java.rmi.NoSuchObjectException异常。

  8.Home方法

  实体Bean的主接口可以定义一个或多个主方法。主方法是不特定于某个实体Bean实例业务逻辑方法。主方法的名字必须以“create”、“ind”或“remove”作为开关。主方法的参数被实体Bean的实现类使用,不依赖于某个特定的实体Bean实例。方法的参数和返回值必须是RMI-IIOP的合法类型。
  主方法的定义必须招聘java.rmi.RemoteException异常。也可以包括其它引用级异常。
  下面代码片断演示两个方法:
  public Interface EmployeeHome extends javax.ejb.EJBHome{
   ...
   public float livingIndex(String state,float Salary)
    throws RemoteExcetption;
   public void addBonus(float company_share_index)
    throws RemoteException,ShareIndexOutOfRangeException;
   ...
  }

  9.主键和对象标识

  每个实体对象有一个惟一的标识。如果两个实体对象有相同的主键,则它们是同一的。EJB架构允许主键是任何合法类型的类。
  拥有实体对象远程接口引用的客户端可以通过getPrimaryKey()方法获取主键标识。和引用联系的对象标识在被引用期间不会改变。
  客户端可以测试两个实体对象是否指向同一个实体对象,用isIdentical(EJBObject)方法。也可以采用equals方法比较它们的主键。
  下面代码片断演示使用isIdentical方法测试指向同一个实体对象的两个对象引用:
  Account acc1 = ...;
  Account acc2 = ...;
  if (acc1.isIdentical(acc2)){
    //acc和acc2是同一个实体对象
  }else{
    //acc1和acc2是不同一个实体对象
  }
  如果客户端知道实体对象的主键,则调用实体Bean主接口的findByPrimaryKey(key)方法,可以获得实体对象的的引用。
  注意,比较两个引用是否指向同一个实体对象,不能使用Object.equals(Object obj)方法,只能使用isIdentical方法。

  10.实体Bean的远程接口

  客户端通过实体Bean的远程接口访问实体对象。实体Bean的远程接口必须扩展javax.ejb.EJBObject接口。远程接口定义客户端使用的逻辑方法。
  下面代码片断演示实体Bean远程接口的定义:
  public Interface Account extends javax.ejb.EJBObject{
   void debit(double amount)
    throws java.rmi.RemoteException,InsufficientBalanceException;
   void credit(double amount)
    throws java.rmi.RemoteException;
   double getBalance()
    throws java.rmi.RemoteException;
  }
  javax.ejb.EJBObject接口定义允许客户端使用实体对象引用进行,如下操作:
  ·获取实体对象的主接口。
  ·删除实体对象。
  ·获取实体对象句柄。
  ·获取实对象主键。
  容器实现javax.ejb.EJBObject接口中定义的方法,而业务方法以EJB的形式,以代理的方式被调用。
  注意 实体对象没有把javax.ejb.EnterpriseBean中定义的方法暴露给客户端,这些方法是被容器调用的。

  11.实体Bean的句柄

  实体对象的句柄是在网络上标识实体的,拥有实体对象的远程接口客户端可以通过调用getHandle()方法获取实体对象句柄,该句柄类继承java.io.Serializable,所以客户端可以序列化句柄。客户端可以在另一个进程和消息系统中,反序列化该句柄,以重新获得实体对象的引用。
  客户端代码必须使用javax.rmi.PortableRemoteObject.narrow(...)方法来把getEJBObject()方法的结果转换成实体Bean远程接口类型。
  下面代码片断演示了句柄的使用:
  //客户端获取account实体句柄,并保存
  ObjectOutputStream stream = ...;
  Account account = ...;
  Handle bandle = account.getHandle();
  stream.writeObject(handle);
  //客户端可以从存储设备中读取句柄,使用句柄获取
  //account实体对象的引用
  ObjectInputStream stream = ...;
  Handle handle=(Handle)stream.readObject(handle);
  Account account = (Account)javax.rmi.PortableRemoteObject.narrow(
  handle.getEJBObject(),Account.class);
  account.debit(100.00);

  12.实体主句柄

  EJB规范允许客户端获取主接口的句柄。客户端把实体Bean的主接口引用以句柄的方式存到存储设备中,然后可以重新获取。当客户端不知道主接口的JNDI名,又想使用主接口,这是个解决的办法。
  主接口的句柄必须实现javax.ejb.HomeHandle接口。
  客户端代码必须使用java.rmi.PortableRemoteObject.narrow(...)方法来把getEJBHome()方法的结果转化成主接口类型。