第2课
简单Entity Bean

[<<返回] [目录] [下一个>>]

本课将扩展第一课中的范例来讲述如何使用bean。BonusServlet 调用entity bean 来将社会安全号码和bomus信息保存到数据库表中,并可从中检 索。这个数据库访问功能将第四层和最后一层添加到第一课介绍的多层例子的瘦 客户中。

随J2EE SDK提供有Cloudscape数据库,不需要对你的环境进行设置便可以使 entity bean对它进行访问。在这个范例中,你无须编写任何SQL或JDBC代码来 创建数据库表格或进行数据库访问操作。在安装和部署的过程中会生成数据库表, 部署工具也生成SQL代码。在第7课“JDBC技术 和“Bean-managed persistence” 中将介绍如何为entity bean编写SQL代 码。

创建entity bean

一个entity bean代表存储在一个数据库表一行中的持久性的数据。当一个 entity bean被创建时,该数据被写入到相应的数据库表行中,如果entity bean 中的数据被更新,则相应的数据库表格行中的数据也被更新。在进行这些数据库 表格创建和行的更新时不需要你编写SQL或JDBC代码。

entity bean数据是永久性的,因为它可以不受服务器崩溃的影响。

  • 如果由于entity bean中的数据正在更新时发生崩溃事故,entity bean数 据会自动回滚到上一次数据库事务处理时的状态。
  • 如果在数据库事务处理期间发生崩溃事故,则该事务处理会被回滚,以防 止因为局部提交使数据受到破坏。

BonusHome

第一课中的CalcHome session bean 代码和本课 (以下)中的BonusHome entity bean代码的主要不同 在于findbyPrimaryKey方法。这种寻找方法将主关键 字作为参数。在本实例中,主关键字是社会安全号码,用来检索带有与该社会安 全号码相对应的主关键字值的表格行。

create方法将bonus值和主关键字作为参数。当 BonusServlet实例化home 接口并调用 create方法时,容器会创建一个BonusBean 实例并调用它的ejbCreate方法。 BonusHome.createBonusBean.ejbCreate 方法必须有相同的签名,以便bonus和主关键字值能够通过entity bean的容器从 home 接口被传递给entity bean。 如果一个指定的主关键字(社会安全)号码行 已经存在,就会抛出一个需要在BonusServlet客户代 码中处理的java.rmi.RemoteException

package Beans;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.FinderException;
import javax.ejb.EJBHome;

public interface BonusHome extends EJBHome {
  public Bonus create(double bonus, String socsec)
        throws CreateException, RemoteException;
  public Bonus findByPrimaryKey(String socsec)
        throws FinderException, RemoteException;
}

Bonus

在创建了home 接口之后,容器会创建远程接口和entity bean。 Bonus 接口声明了getBonusgetSocSec 方法,以便Servlet可以从该entity bean检索到数据。

package Beans;

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface Bonus extends EJBObject {
  public double getBonus() throws RemoteException;
  public String getSocSec() throws RemoteException;
}

 

BonusBean

BonusBean 是一个container-managed的entity bean。 这就是说,容器会对数据进行维护,并且在无须你编写代码在entity bean和数据库间传递数据或定义事务边界的情况下管理事务。

如果由于某种原因,你想让entity bean来管理它自己的数据维护或事务,就 需要按照以下BonusBean编码来实施一些空方法。下面 的参考将向你介绍有关bean-managed persistence和事务处理。

  • “编写高级应用”教程中的第三章。
    developer.java.sun.com/developer/onlineTraining/Programming/JDCBook
  • “Java 2 企业版开发人员指南”中的第四章。
    java.sun.com/j2ee/j2sdkee/techdocs/guides/ejb/html/DevGuideTOC.html

BonusServlet调用BonusHome.create 时,容器调用BonusBean.setEntityContext方法。被传 递给setEntityContext方法的 EntityContext实例拥有使bean返回给本身一个引用 或获得它的主关键字的方法。

接下来,容器调用ejbCreate方法。ejbCreate 方法将数据分配给bean的实例的变量,然后,容器将该数据写入到数据库中。在 ejbCreate方法之后是对ejbPostCreate 方法的调用,用于在bean创建后的相关处理。这个简单的范例不进行创建后的处 理。

其它空方法作为回调方法被调用,以通知bean将发生某种事件。如果你使用的 是bean-managed persistence,还得提供某些方法中的行为,如果需要进行始化 操作或bean-specific的清除,则得为余下的方法提供行为。这些清除和初始化操 作在bean运行周期的某些特定时间发生,容器在适当的时候会通知bean并调用可 用的方法。以下是对空方法的简单描述:

  • 容器在将bean从存储中换入/换出之前对getBonus和getSocCec方法进行调 用。这个过程与存储页面在内存和硬盘间进行交换的虚拟内存概念十分相似。
  • 如果home 接口有一个对应的remove方法(由客 户对其进行调用),则由容器调用ejbRemove方法。
  • 容器在将bean的状态与基础数据库协调一致之前,就对ejbLoadejbStore方法进行调用。

客户可通过调用getBonusgetSocSec 方法来检索存储在实例变量中的数据。这个范例中没有set< type > 方法,但如果有,客户就可以调用它们来修改bean实例变量中的数据。对实例变 量的任何修改都可能导致基础数据库中表格行的更新。

package Beans;

import java.rmi.RemoteException;
import javax.ejb.CreateException;
import javax.ejb.EntityBean;
import javax.ejb.EntityContext;

public class BonusBean implements EntityBean {

  public double bonus;
  public String socsec;
  private EntityContext ctx;

  public double getBonus() {
    return this.bonus;
  }
  public String getSocSec() {
    return this.socsec;
  }

  public String ejbCreate(double bonus, 
		String socsec) 
		throws CreateException{
  //Called by container after setEntityContext 
    this.socsec=socsec;
    this.bonus=bonus;
     return null;
  }

  public void ejbPostCreate(double bonus,
                            String socsec) {
  //Called by container after ejbCreate
  }

//These next methods are callback methods that
//are called by the container to notify the
//Bean some event is about to occur

  public void ejbActivate() {
  //Called by container before Bean 
  //swapped into memory 
  }

  public void ejbPassivate() {
  //Called by container before 
  //Bean swapped into storage 
  }

  public void ejbRemove() throws RemoteException {
  //Called by container before 
  //data removed from database
  }

  public void ejbLoad() {
  //Called by container to 
  //refresh entity Bean's state
  }

  public void ejbStore() {
  //Called by container to save 
  //Bean's state to database
  }

  public void setEntityContext(EntityContext ctx){
  //Called by container to set Bean context
  }

  public void unsetEntityContext(){
  //Called by container to unset Bean context
  }
}

修改Servlet

关于initdoGet 方 法的修改,本课中的BonusServlet代码与第一课中的相似。 本课中的init 方法除了用于查找CalcBean session bean,还可以查 找BonusBean entity bean。

public class BonusServlet extends HttpServlet {
  CalcHome homecalc;
  BonusHome homebonus;
  Bonus theBonus, record;

public void init(ServletConfig config) 
		throws ServletException{
  try {
    InitialContext ctx = new InitialContext();
    Object objref = ctx.lookup("bonus");
    Object objref2 = ctx.lookup("calcs");
    homebonus=(
          BonusHome)PortableRemoteObject.narrow(
          objref, BonusHome.class);
    homecalc=(CalcHome)
          PortableRemoteObject.narrow(
          objref2, CalcHome.class);
  } catch (Exception NamingException) {
    NamingException.printStackTrace();
  }
}

doGet 方法中的try语 句创建CalcBeanBonusBean home 接口。在调用calcBonus计算bonus之后, BonusHome.create 方法被调用在基础数据库表格中创 建一个entity bean实例和一个对应的行。在创建了表格后,调用 BonusHome.findByPrimaryKey 方法来通过主关键字 (社会安全号码)来检索相同的记录。 下一步,一个HTML页面被返回到浏览器, 显示原来传入的数据, bonus的计算值以及从数据库表格行中检索的数据。

catch 语句获取和处理复制的主关键字值(社会安 全号码)。基本数据库表不能有两行具有相同的主关键字,因此,如果你传入了 相同的社会安全号码,Servlet就会在创建entity bean之前捕捉和处理相关错误。 在出现重复关键字的事件时,Servlet就会返回一个HTML页面,其中包括原来传入 的数据、bonus的计算值和一个重复关键字的错误通知信息。

try {
    Calc theCalculation; 
//Retrieve Bonus and Social Security Information
    String strMult = request.getParameter(
                 "MULTIPLIER");//Calculate bonus
    Integer integerMult = new Integer(strMult);
    multiplier = integerMult.intValue();
    socsec = request.getParameter("SOCSEC");
//Calculate bonus
    double bonus = 100.00;
    theCalculation = homecalc.create();
    calc = theCalculation.calcBonus(
         multiplier, bonus);
//Create row in table
    theBonus = homebonus.create(calc, socsec);
    record = homebonus.findByPrimaryKey(socsec);
//Display data
    out.println("<H1>Bonus Calculation</H1>");
    out.println("<P>Soc Sec passed in: " + 
        theBonus.getSocSec() + "<P>");
    out.println("<P>Multiplier passed in: " + 
        multiplier + "<P>");
    out.println("<P>Bonus Amount calculated: " + 
		 theBonus.getBonus() + "<P>");
    out.println("<P>Soc Sec retrieved: " + 
		  record.getSocSec() + "<P>");
    out.println("<P>Bonus Amount retrieved: " + 
		 record.getBonus() + "<P>");
    out.println("</BODY></HTML>");
//Catch duplicate key error
  } catch (javax.ejb.DuplicateKeyException e) {
    String message = e.getMessage();
//Display data
    out.println("<H1>Bonus Calculation</H1>");
    out.println("<P>Soc Sec passed in: " + 
    socsec + "<P>");
    out.println("<P>Multiplier passed in: " + 
        multiplier + "<P>");
    out.println("<P>Bonus Amount calculated: " + 
		  calc + "<P>");
    out.println("<P>" + message + "<P>");
    out.println("</BODY></HTML>");
  } catch (Exception CreateException) {
    CreateException.printStackTrace();
  }
}

编译

首先, 编译entity bean和Servlet。 参考第一课进行路径和类路径的设置, 以及源文件的位置信息。

编译entity bean

Unix

#!/bin/sh
cd /home/monicap/J2EE
J2EE_HOME=/home/monicap/J2EE/j2sdkee1.2.1
CPATH=.:$J2EE_HOME/lib/j2ee.jar
javac -d . -classpath "$CPATH" Beans/BonusBean.java
           Beans/BonusHome.java Beans/Bonus.java

Windows

cd \home\monicap\J2EE
set J2EE_HOME=\home\monicap\J2EE\j2sdkee1.2.1
set CPATH=.;%J2EE_HOME%\lib\j2ee.jar
javac -d . -classpath %CPATH% Beans/BonusBean.java 
           Bean s/BonusHome.java Beans/Bonus.java

编译Servlet

Unix:

cd /home/monicap/J2EE/ClientCode 
J2EE_HOME=/home/monicap/J2EE/j2sdkee1.2.1
CPATH=.:$J2EE_HOME/lib/j2ee.jar:/home/monicap/J2EE
javac -d . -classpath "$CPATH" BonusServlet.java 

Windows:

cd \home\monicap\J2EE\ClientCode
set J2EE_HOME=\home\monicap\J2EE\j2sdkee1.2.1
set CPATH=.;%J2EE_HOME%\lib\j2ee.jar;
          \home\monicap\J2EE
javac -d . -classpath %CPATH% BonusServlet.java

启动平台和工具

为了运行这范例,你需要启动J2EE服务器、部署工具和Cloudscape数据库。在 不同的窗口中,键入以下命令:

j2ee -verbose
deploytool
cloudscape -start

如果不成功,则可从J2EE目录中键入。

Unix

j2sdkee1.2.1/bin/j2ee -verbose
j2sdkee1.2.1/bin/deploytool
j2sdkee1.2.1/bin/cloudscape -start

>Windows

j2sdkee1.2.1\bin\j2ee -verbose
j2sdkee1.2.1\bin\deploytool
j2sdkee1.2.1\bin\cloudscape -start

装配和部署

步骤为:

  • 更新应用文件
  • 创建Entity Bean

更新应用文件

WAR文件包含BonusServletbonus.html。 由于已经对BonusServlet作了改变, 因此,必须用 新的Servlet代码更新J2EE应用。

  • 本地应用窗口:突出(highlight)BonusApp 应用。
  • 工具菜单: 选择更新应用文件。 .
注: 上一课的BonusApp 应用被自动卸载。

创建Entity Bean

为entity bean创建EJB JAR的步骤与第一课涉及的session bean的步骤很相 似。仅有几处不同, 下面将对这些不同之处加以解释。

注: 在这一课中,entity bean 与session bean分别在两个不同的JAR文件中,本课中延续了第一课中的范例,只 作了很小的改动。因为这些bean有相关的功能,你可以将它们捆绑并部署到同一 个JAR文件中。第3课将介绍如何把这些相关的bean捆绑到同一个JAR文件中。

文件菜单:

  • 选择新enterprise Bean

介绍 :

  • 阅读,然后点击“下一步”。

EJB JAR :

  • 确定“Enterprise Bean will go in field”中显示的是 BonusApp
  • 指定BonusJar作为显示名称。
  • 点击“添加”(与“内容”窗口临近的“添加”按钮)。

将内容添加到JAR:

  • 切换目录,使beans目录显示其内容。
  • 选择Bonus.class
  • 点击“添加”。
  • 选择 BonusBean.class
  • 点击“添加”。
  • 选择BonusHome.class
  • 点击“添加”。
  • 点击“OK”。

 

图11 : 将类添加到BonusJar

EJB JAR :

  • 点击“下一步” 。

通常: :

  • Beans.BonusBean是类名称
  • Beans.BonusHome是Home接口
  • Beans.Bonus是远程接口
  • 输入BonusBean作为显示名称
  • 点击Entity
  • 点击“下一步”

Entity 设置:

  • 选择Container-Managed persistence
  • 在窗口底部,选定bonussocsec
  • 为主关键字类指定java.lang.String。 注意, 这个主关键字必须是一个类类型。原始类型对于主关键字无效。
  • 为主关键字域名指定socsec
  • 点击“下一步”。

环境项目:

  • 点击“下一步”。 这个简单的entity bean没有使用这个属性(环境项目)。

Enterprise Bean 引用s:

  • 点击“下一步”。 该简单entity bean不引用其它enterprise bean。

资源引用:

  • 点击“下一步”。 该简单entity bean不查找数据库或JavaMail会话对象。

安全:

  • 点击“下一步”。 该简单entity bean不使用安全角色。

事务管理 :

  • 选择Container-managed transactions (如果 还没有被选择)。
  • 在下面的列表中需要create, findByPrimaryKey, getBonusgetSocSec方 法。这就是说,容器在运行这些方法之前启动新的事务流程。这个事务流程正 好在这些方法结束后提交。可以在“企业JavaBeans开发人员手册”中的第六章 中获得有关这些事务流程的更多信息 ( java.sun.com/j2ee/j2sdkee/techdocs/guides/ejb/html/DevGuideTOC.html)。

 

图12 事务管理

  • 点击“下一步”。
  • 点击“完成”。

本地应用 :

  • 选择BonusApp
  • 在检查窗口中,选择JNDI names 名称。
  • 赋予BonusBean的JDNI名字为bonus
  • 按下回车键。

在部署J2EE应用之前,需要为entity bean指定部署设置,生成SQL。 具体如 下:

本地应用窗口:

  • 选择BonusBean

检查窗口:

  • 选择Entity
  • 点击右下方的Deployment Settings按钮。

部署设置:

  • 指定jdbc/Cloudscape(Cloudscape中的C 为大写)为数据库JNDI名称。
  • 按下回车键。
  • 确保Create table on deployDelete table on Deploy已经被选定。
  • 点击Generate SQL
注: 如果出现连接被拒绝的错 误,则应按照“启动平台和工具”中描述的 方法启动数据库。

 

图13 : 生成SQL和数据库表

  • 当SQL生成成功后,选择EJB方法框中的findByPrimaryKey 方法。右侧会出现一个SQL描述,内容应该是:SELECT “socsec” FROM “BonusBeanTable” WHERE “socsec”=?。问号代 表传递给findByPrimaryKey方法的参数。
  • 点击“ok”。

验证和部署J2EE应用

验证:

  • 选定BonusApp, 从“工具”菜单中选择“验证器”。
  • 在弹出的对话框中,点击“OK”。 窗口将通知 你测试成功。
  • 关闭验证器窗口,准备部署应用。
注: 在Version 1.2软件中,可 能会出现一个 tests app.WebURI错误,但并不影响 J2EE应用的部署。

部署:

  • 工具菜单: 选择Tools.Deploy Application
注: 不要选择“Return Client Jar”选项。只有在为客户程序部署独立的应用时才选择这个选项。本范例使用了 一个Servlet和HTML页面,因此,不应该选择这个选项。若选择这个选项,会生成 一个带有独立式应用所需部署信息的JAR文件。
  • 点击“下一步”。 确保JNDI名称的正确显示,即CalcBeancalcsBonusBeanbonus。 自己键入丢失的JNDI名称,然后按回车键。
  • 点击“下一步”。 确保“上下文根目录”名称显示为BonusRoot。 如果不是,则需要将其键入,然后按回车键。
  • 点击“下一步”。
  • 点击“完成”开始部署。
  • 部署完成后,点击“OK”。

运行J2EE应用

web 服务器的运行端口默认为8000。 若要打开bonus.html 页面,可进入以下地址, http://localhost:8000/BonusRoot/bonus.html, 这是部署工具放置HTML文件的位置。

填入社会安全号码和乘数,点击“提交”按钮。BonusServlet 处理你传入的数据,并返回一个带bonus计算值的HTML页面。

Bonus Calculation

Soc Sec passed in: 777777777
Multiplier passed in: 25
Bonus Amount calculated: 2500.0
Soc Sec retrieved: 7777777777
Bonus Amount retrieved: 2500.0

如果返回到bonus.html,并将乘数改为2,但使用 同一个社会安全号码,你将会看到:

Bonus Calculation
Soc Sec passed in: 777777777
Multiplier passed in: 2
Bonus Amount calculated: 200.0
Duplicate primary key.

[TOP]

 

常见问答
下载中心
产品简介
 
 
Solaris论坛
 
   
 
null