创建EJB组件

816-7864-10

Table of Contents Table of Contents Next Chapter

第3章


开发会话 Bean

翻译:Smiling_Sundoc:田大海

你可以使用Sun ONE Studio 4 IDEEJB Builder来开发代表Server端业务逻辑的会话 bean本章讲述了创建无状态和有状态会话 bean的过程。

任何类型的会话 bean都可以用EJB container来管理事务或者你自己写代码来管理事务。会话 bean通过JDBC API或者Java Transaction API (JTA)来访问持久的数据。一个会话 bean能管理一个或多个实体bean

IDE提供了向导(wizard)来帮助你创建企业bean的各个部分:一个bean class、两个或四个接口。缺省包括一个remote 接口 和一个home 接口,但是你可以替换或添加一个local 接口和一个local home 接口。大多创建会话 bean的过程被自动化了。

当你对会话 bean编程时,除了本章描述的选项外,还有许多其它选项。Sun Java System Studio IDE能帮你做很多编码工作,它还支持很多灵活的选项,让你有很多决策空间。要了解对会话 bean详细的编码指导,请参照《开始之前》中相关资源或者参照企业bean编程的有关文章。


用 EJB Builder 编写会话 Bean

EJB Builder是由一系列的向导、属性页、编辑器组成的,你可以用它轻松一致地编写企业bean。要了解你的系统是否安装了EJB Builder,到主窗口中,选择Tools right arrow Options right arrow IDE Configuration right arrow System right arrow Modules right arrow J2EE Support。如果你在模块(module)列表中看到了EJB 2.0 Builder,并且属性页的Enabled域设置为True,你就可以使用EJB Builder了。

Sun Java System Studio IDE中你可以采用多种方法来创建会话 bean。然而如果采用本章推荐的方法,你将获得最全面的支持,并且也是最快的方法。这里提到的方法充分利用了IDE的能力来保证一致性和对J2EE规范的支持。

要得到最好的结果,用EJB Builder编写会话 bean如下:

l        创建会话 bean和它要求的类

当你完成了EJB Builder向导要求的步骤后你就得到了会话 bean的一个框架。会话 bean的三个(或五个)类和逻辑节点就出现在ExplorerFilesystem 面版中。向导产生了所有这些类的声明,需要你来提供这些方法的实现。逻辑节点是你和会话 bean打交道的最好位置。所有的逻辑节点在Explorer中用图标 Logical node icon. 来表示。

l         添加方法、参数和异常

利用IDEGUI支持你可以利用上下文菜单中弹出的对话框来添加方法,也可以通过直接编辑相应的类来实现。

l         设置会话 bean部署描述文件中的值

通过修改逻辑节点中会话 bean属性页中的属性来完成。

从会话 bean的逻辑节点你可以验证它自己。还可以通过IDE的有关测试手段来测试它。


选择会话 Bean 的类型

会话 bean负责处理clientapplication service的交互交互的持久时间就是该会话的时间。你该采用哪种类型— 有状态 还是无状态?让container来管理事务还是让它自己管理事务?下面会讨论如何作出选择。

EJB Builder能支持所有这些选择,并且你可以用同一个向导来产生会话 bean的基础框架。最后你再声明会话 bean的类型。

详细信息参照下面小节的内容:

无状态 还是有状态 会话 Bean

会话 bean的主要目的是代表client执行有关工作。也就是说代表clientserver端与一个或多个实体bean执行一次对话conversation。当这个对话包含多个简单的问题和回答时,对话的管理者(也就是会话 bean)必须能够记住这些信息直到对话结束。这种情况下,会话 bean必须要有状态。而无状态 会话 bean管理一个相对不是很复杂的对话。

关于如何选择的更详细的讨论参见第二章。表3-1列举了设计中应该考虑的有关因素。

3-1       采用无状态 还是 有状态 会话 Bean

考虑因素

无状态

有状态

范围(Scope)

管理client和entity之间的一个简单交互,并且一个会话只调用一个方法。

管理client和entity间更复杂的交互,并且一个会话调用一个或多个方法。

初始化

没有必须要初始化的数据

有状态会话 bean 状态必须初始化。例如,如果它要访问一个远程资源,需要获得一个对资源工厂的引用。

保存的信息

在会话期间, 不保存方法调用间的状态信息。

在会话期间,保持client 和server的对话状态.它保存方法调用间的状态信息。会话结束时抛弃这些信息。

client的关系

一个无状态 bean instance 一次只为一个client执行一个操作. 它完成了方法调用后, 这个instance 被放入缓冲池中并能分配给另一个不同的client,即使在同一个会话中也是如此。

一个有状态 bean instance一次能为一个client执行一系列操作。当client会话完成后,这个instance被销毁,而不是放到缓冲池中。

应用例子

无状态 bean能代表一个产品目录视图。它的一个方法可以让最终用户查找在线产品目录中的某个产品。

有状态 bean 能够代表一个在线购物车,可以调用若干个方法来填加要买的产品,直到最终用户要处理整个定单。

Container-Managed还是Bean-Managed事务

第二章中详细论述的那样, 你必须声明用那种方式来管理事务。表3-2概括了这些选择间的差异。

3-2      采用Container-Managed 还是Bean-Managed事务

考虑因素

Container-Managed 事务

Bean-Managed 事务

事务边界是如何设定的

EJB container根据Java 2 Platform, Enterprise Edition Specification来决定什么时候开始和提交事务。

由程序员显示地对事务边界进行编码,从而获得对事务的细粒度控制。

事务管理器

EJB container自己是事务管理器

JTA来管理事务,JTA能包含对其它资源的事务如JDBC。

事务和方法的关系

每个方法只允许一个事务。然而一个方法不是必须和某个事务关联。

情况更为复杂,但是你可以在一个方法中编写多个事务。

对采用container-managed事务CMT的企业beancontainer在方法将要执行前开始一个事务,并在该方法将要退出前提交该事务。采用CMT,你可以让client来控制事务。例如,client可以通过一个有状态 CMT 会话 bean调用不同的方法来串起一个逻辑的商业事务。

对采用bean-managed事务(BMT)的企业bean,你必须显示地在代码中声明一个事务的开始和结束。


定义会话 Bean

EJB Builder向导能自动创建会话 bean缺省的三个类一个bean class、你选择的接口remote local或者二者都有)。采用下面的步骤来定义会话 bean

1.       选择或创建一个包来存放会话 bean

2.       EJB Builder 向导来生成 会话 bean的基础框架

3.       添加create方法和业务方法

4.       编写所添加方法的方法体

这些基本的步骤下面会有详细的解释。

创建包

要创建一个包来存放会话 bean选择一个filesystem,右击选择New Package

启动EJB Builder 向导

创建会话 bean,按下列步骤进行:

1. IDE的主窗口中, 选择View right arrow Explorer打开 Explorer窗口。

2. ExplorerFilesystems屏面上,选择会话 bean要存放的包。

3.右击并选择New right arrow J2EE right arrow 会话 EJB

系统弹出EJB Builder向导。注意左边的面板显示了创建会话 bean当前的步骤和将要完成的步骤。

产生缺省的会话 Bean

EJB BuilderSession Bean Name and Properties屏面上你必须就状态、事务类别、接口类型作出选择。按下列步骤进行:

1. 输入会话 bean的名字,选择会话 bean的类型

选择适当的按钮状态、事务类别、接口类型作出选择。图3-1显示了你的选择。注意它们的缺省值 Stateless、Container-Managed、Remote Interface Only。

3-1 无状态 ( 有状态 BMT) 会话 Bean向导选项示例

Screenshot showing the EJB Builder Wizard's first pane and selections for a session bean.


注意在向导第一个屏面上的选项决定了向导如何生成代码。如果以后你想改变这些最基本的选择,可以用bean的属性页来完成,参照第八章


2. 单击 Next

系统打开会话 Bean Class Files屏面,下图为无状态 会话 bean

Screenshot showing the EJB Builder Wizard's second pane, listing the classes about to be generated for a session bean.

对于有状态 会话 bean来说,你还有另外一个选择要做:会话同步。

Screenshot showing part of the EJB Builder Wizard's second pane, with the Implement Session Synchronization Interface checkbox selected.

这个选项在表3-4和使用会话同步中有解释。

3. 检查bean class和接口如果需要改变它们。

组成会话 bean的类以及它们所在的路径显示在这个屏面上。

  • 你可以改变bean所在包的位置。
  • 你可以用Modify按钮来改变任何类的名字,指定一个已经存在的类或创建一个新的类。例如,你可以实现一个home接口和remote 接口都已经申明过的bean,现在你需要做的是生成一个新的bean class

如果你指定了命名的包以外的任何类,这些类将以不同于图3-2中的形式显示出来。

  • 不要改变接口的超类 (IDE的代码生成器委托超类的实现,如果存在的话。然而,一般来说,你应该检查这些代码)

在你改变这些域之前,同样考虑以下几点:

  • 服务器要求。EJB Builder向导允许你把会话 bean的一部分移到其他的位置。例如,你可以改变一个或多个相关对象的包的名字,这样bean class在一个目录中,而home remote接口在其它的目录中。首先,你应该了解你的应用服务器是否支持这种文件的分布。
  • 类的重用。在这一点上,如果你愿意的话,可以从其它的会话 bean中替换bean class home remote接口。如果你替换的类少了任何方法或异常,向导会提醒你。
  • 包和目录命名。使用有效的java标识符。

4. 完成这些步骤后单击Finish。

向导自动生成会话 bean 的基础框架所包含的部分,下面要分别讨论它们。


查看会话 Bean 的类

EJB Builder向导产生会话 bean 缺省的类,并建立了这些类之间的关系。图3-2说明了一个典型的会话 bean (它的所有类在同一个包中)如何在 Explorer 的 Filesystems 屏面上显示的。

3-2包含remote接口的会话 Bean的缺省类

Screenshot showing the typical classes generated for a session bean.

用类图标 Class node icon. 标记的节点代表了会话 bean的类。用bean图标 Bean icon. 标记的节点是会话 bean的逻辑节点。在逻辑节点上进行所有的编辑工作。对样例中bean的主节点描述如下:

  • remote 接口扩展了javax.ejb.EJBObject接口并提供了会话 bean业务方法的签名。
  • bean class 扩展了javax.ejb.SessionBean接口并实现了会话 bean的方法。
  •         home接口扩展了 javax.ejb.EJBHome接口并提供了会话 bean create方法的签名。
  • 如果你选择了本地接口,就会看到一个标记为Localbean_name的节点。这个接口扩展了javax.ejb.EJBLocalObject接口并提供了会话 bean业务方法的签名。
  • 如果你选择了local home 接口,就会看到一个标记为Localbean_nameHome的节点,这个接口扩展了javax.ejb.EJBLocalHome接口并提供了会话 bean create方法的签名。
  • Explorer中的逻辑节点用来组织企业bean的所有元素,让你更方便地对他们进行操作。

展开节点

当你展开会话bean包节点下面的四个节点时,它们象树图一样,参照图3-3。

3-3 包含remote接口会话 BeanExplorer详细视图

Screenshot showing an expanded view of the typical classes generated for a session bean.

回顾产生的类

EJB Builder自动生成了一个create方法和几个生存周期方法。下面我们来讨论这些方法。

缺省的create方法

向导在每个会话 bean class里面产生一个如下的ejbCreate方法的签名

public void ejbCreate() { 
} 

相应的create方法放在了会话 beanhome接口里面

public interface AcctBalHome extends EJBHome { 
        public AcctBal create() 
                       throws RemoteException, CreateException; 
} 

要了解更多信息,参照完成Create方法

生存周期方法

向导在任何会话 beanbean class里面产生了如下的生存周期方法。

public void setSessionContext(SessionContext context) { 
        this.context = context; } 
public void ejbActivate() {
} 
public void ejbPassivate() { 
} 
public void ejbRemove() { 
} 

表3-3列举了会话 bean中这些方法作用。

3-3 会话 Bean Class中生存周期方法的作用

方法

作用

setSessionContext

这个方法中可以让你在一个域变量(field)中存储对SessionContext的引用,并能给实例变量赋值.你可以用它对整个会话 bean生存周期中用到的资源进行分配。比如数据库的连接工厂。缺省情况下EJB Builder 向导产生代码将SessionContext赋值给域变量context

ejbActivate

该方法用来初始化 bean, 获得该实例所需要的资源。

ejbPassivate

bean实例钝化之前,该方法释放所用的资源。

ejbRemove

该方法释放在ejbCreate和业务方法中获得的资源。

如果你选择让会话 bean采用会话同步接口向导会在bean class里面产生另外三个方法

public void afterBegin() { 
} 
public void beforeCompletion() { 
} 
public void afterCompletion(boolean committed) { 
} 

关于会话同步的解释请参照表3-4。

3-4       会话 Bean Class中会话同步方法的作用

方法

目的和作用

afterBegin

这个方法告诉该实例一个新的事务已经开始。EJB container在将要调用业务方法之前调用它。在afterBegin中,你可以从数据库中加载实例变量。

beforeCompletion

这个方法告诉实例一个业务方法已经完成,但是事务还没有提交。这是会话 bean回滚该事务的最后机会。如果数据库还没有用实例变量来更新,你可以把更新代码写到这个方法的方法体中。

afterCompletion

这个方法告诉实例一个事务已经完成。在它的参数里面,布尔值true 意味着事务已经提交,false意味着事务被回滚。如果事务失败并被回滚,可以通过该方法让会话 bean 利用数据库中的值来刷新它的实例变量的值。


完成会话 Bean 的创建

完成会话 Bean的创建步骤随着bean的种类不同而不同。指导方针如下:

采用推荐的方法处理企业bean

附录A讲述了对企业bean处理应该采用的最好方法。以及你采用其它方法可能出现的错误和异常。

一般来讲,你应该通过逻辑节点而不是类节点来处理企业bean。通过bean的属性页或Customizer对话框来编辑方法。当不能由某个对话框来编辑时,用IDESource Editor来完成和编辑任何bean的代码。

完成Create方法

如果你的bean无状态型的只有一个没有任何参数的create方法。无状态 会话 bean中不含有任何特定用户或特定client的数据。

如果你的bean有状态型的,它可以含有一个或多个可带参数的create方法。

在任何情况下,在逻辑节点下进行工作。在Source Editor打开create方法(通过选择标记为create()的节点,右击并选择Open来实现)。在Source Editor中完成create方法。

完成无状态 Bean Create方法

在无状态 会话 bean中,create方法经常被用来和某些资源建立连接。例如这个方法可以用来look up一个资源工厂的引用,并把它存放在一个域变量中,这样以后的方法调用中可以通过它获得JDBC连接。

完成有状态 Bean Create方法

有状态 会话 bean中,你可以用create方法中的参数来look up资源工厂的引用,或传送一个特定 client 的信息(例如用户名和密码),见代码例子3-1。这个方法可以存储信息供以后使用。 注意这个 create 方法使用了一个helper class,IdVerifier

代码例子3-1 一个有状态 会话 Bean中的Create方法

public void ejbCreate(String userid, String pwd) 
        throws CreateException {
 
        if (userid == null) {
                       throw new CreateException("Please enter a user ID.");
        }
        else {
                       this.userid = userid;
        }
 
        IdVerifier idChecker = new IdVerifier();
        if (idChecker.validate(pwd)) {
                       this.pwd = pwd;
        }
        else {
                       throw new CreateException("Invalid password: " + pwd);
        }
 
        contents = new Vector();
}

在有状态 Bean中增加Create方法

在有状态 Bean中增加Create一个或多个方法,按下面步骤来做:

1.选择bean的逻辑节点,右击并选择Add Create Method。

系统弹出Add Create Method对话框

2. 输入一个以create开始的方法名,如果需要添加参数和异常,单击OK。

Create方法的签名生成在home接口中,对应的ejbCreate方法生成在bean class里面。

3. Source Editor中完成编码工作。

在bean的逻辑节点中,展开Classes节点,选择Bean Class,右击并选择Open。

完成生命周期方法

EJB Builder为你产生了四个生命周期方法。对无状态 会话 bean来说这四个方法已足够了。对有状态 会话 bean来说,你可能需要在下面两个方法中添加有关代码:ejbPassivate ejbActivate

例如,有状态 bean中可能含有不能序列化的域变量通过取代引用而变的能序列化)。或者bean的对话状态中含有打开的资源(在bean的实例被钝化时,container不能保持该状态)。对每种情况,你必须完成ejbPassivate方法来释放不能序列化的域变量。然后完成相应的ejbActivate方法来恢复这些域变量。

完成ejbPassivate方法

这个方法为域变量的序列化作准备。如代码例子3-2所示,你必须在这个方法中关闭所有的JDBC连接,并给存储连接的域变量赋值为null

代码例子3-2 ejbPassivate 方法

public void ejbPassivate() {
 
        try {
            con.close();
        } catch(Exception ex) {
            throw new EJBException("ejbPassivate Exception: " + 
                                              ex.getMessage());
                       } finally { 
                               con = null; 
        
    } 

完成ejbActivate方法

如代码例子3-3中所示,这个方法使得域变量重新变得可用。

代码例子3-3 ejbActivate方法

public void ejbActivate() {
 
        try {
            InitialContext ic = new InitialContext();
            DataSource ds = (DataSource) ic.lookup(dbName);
            con =  ds.getConnection();
        } catch (Exception ex) {
            throw new EJBException("ejbActivate Exception: " + 
                                              ex.getMessage());
        
    }

增加业务方法

会话 bean你可以增加业务方法来为client执行业务任务。这个方法可能访问数据库,或者管理一个或多个实体bean

要在有状态 会话 bean中增加业务方法,按下列步骤来做:

1. 选择bean的逻辑节点,右击并选择Add Business Method。

系统弹出Add Business Method 对话框。

2. 给方法命名,保证返回值是合适的,如果需要添加参数和异常,单击OK。

业务方法的签名产生在remote接口,对应的方法在bean class中实现。

3. Source Editor中完成编码工作。

在bean的逻辑节点中,展开Classes节点,选择Bean Class,右击并选择Open。

如果会话 bean需要访问数据库,通过在一个数据存取对象(DAO)中封装对数据库的访问,可能会减少JDBC调用次数(也节省了系统资源和网络带宽)。DAO为会话 bean完成实际的数据存取工作。采用DAO能使会话 bean的代码简单明了。也使你的bean不依赖特定厂家的工具或数据库。

对事务进行编码

对事务进行编码依下列的条件不同而不同:会话 bean有状态型还是无状态型,采用BMT还是CMT。下面的指导方针包括声明事务边界,处理回滚,和利用会话同步接口。

了解事务的跨度(span

事务的跨度依会话 bean的类型而有所不同。表3-5对此作了概括。注意CMT加上statefulness给一个bean更多的灵活性。

3-5 事务和方法的关系 

在无状态 BMT bean (自己管理事务)中,一个事务只跨一个方法。

在有状态 BMT bean中,一个事务能跨同一个会话 bean中的一个或多个方法。

在无状态 CMT bean (事务由container管理)中, 一个事务能跨多个方法,但是每个方法必须在不同的会话 bean中

在有状态 CMT bean中,一个事务能跨同一个会话 bean中的一个或多个方法。

声明事务边界和回滚

本节讨论对事务的起始点和终止点编码的指导方针,适用于CMT BMT bean。开始之前,请记住下面的通用规则:

  • 嵌套事务在会话 bean JTA 代码中是不允许的。
  • JDBC JTA 不混合编写时,代码更容易维护。通常JTA 是更可取的选择,因为它包含其它资源的事务,包括JDBC

CMT Bean

CMT bean中,所有事务的边界都由EJB container来设定,这就意味着你不需要声明一个事务的开始和结束。通常EJB container在将要执行一个方法前开始一个事务,在将要退出这个方法前提交这个事务。

不要调用任何可能干扰container事务边界的方法。可能出现问题的方法有:

  • commit, setAutoCommit,java.sql.Connectionrollback方法
  • javax.ejb.EJBContextgetUserTransaction方法
  • javax.transaction.UserTransaction的任何方法

会话 bean可以用两种方法回滚container-managed事务:

  • 如果抛出系统异常,container自动回滚事务。
  • javax.ejb.EJBContextsetRollBackOnly 方法告诉container回滚事务( 尽管只抛出应用异常)。

BMT Bean

BMT bean中,你必须显示地编码来开始和终止一个事务。用javax.transaction.UserTransaction接口来显示地划分事务的边界。下面的代码例子中,使用了JTA接口:

UserTransaction ut = ejbContext.getUserTransaction(); ut.begin();   
 // perform transactional work here      ut.commit();

当在事务中作的更新操作要保存,事务用commit来结束。当事务失败,执行回滚操作,也就是执行事务中操作的反操作。当你对采用BMT的会话 bean执行回滚时,不要采用getRollbackOnly setRollbackOnly方法,这两个方法只是用来和EJB container交互。

采用会话同步

有状态 CMT 会话 bean能够使用会话同步接口,它能使bean更好地控制缓存在事务中的数据库数据。

这个接口提供了一些回调方法,这些方法分别在一个事务开始前、提交前、回滚前被EJB container调用。利用这个接口,在事务的特定阶段,会话 bean 的实例变量能够和数据库中相应的值同步。如果事务没有完成,会话 bean 能回滚实例变量的值。

  • afterBegin container 在事务中调用第一个业务方法前调用它,你可以在这个方法中编写在事务范围内该实例用到的对数据库的任何操作代码。
  • beforeCompletion。当会话 beanclient已完成了当前事务中的操作,在提交给实例用到的资源管理器之前,container调用它。你可以在这个方法里面把缓存的数据库更新写到数据库中。你也可以调用session context setReadbackOnly方法来回滚事务。
  • afterCompletionOntainer调用这个方法来通知当前的事务已经完成。如果事务已经提交,状态设为True。如果事务已经回滚,状态设为False ,你可以在这个方法中手工重新设置实例的状态。

要在会话 bean中增加会话同步接口,在向导中作如下选择:

1.在会话bean向导的第一个屏面中, 在State部分, 选择Stateful。

2. 在会话bean向导的第二个屏面中,选择Implement Session Synchronization Interface。

如果你作了这些选择,类似代码例子3-4中的代码被插入到会话 bean class。在这个例子中checkingBalancesavingBalance变量 afterBegin方法中装入。

代码例子3-4 afterBegin方法样例

public void afterBegin() {
        System.out.println("afterBegin()");
        try {
                       checkingBalance = selectChecking();
                       savingBalance = selectSaving();
        } catch (SQLException ex) {
                       throw new EJBException("afterBegin Exception: " +
                               ex.getMessage());
        }
}

代码例子3-5表明了在afterCompletion方法中,当事务失败回滚后,用数据库刷新account-balance域变量的值。

代码例子3-5 afterCompletion方法样例

public void afterCompletion(boolean committed) {
        System.out.println("afterCompletion: " + committed);
        if (committed == false) {
                       try {
                               checkingBalance = selectChecking();
                               savingBalance = selectSaving();
                       } catch (SQLException ex) {
                               throw new EJBException("afterCompletion SQLException: " +
                                              ex.getMessage());
                       }
        }
}

创建了会话 Bean 以后

要使你的会话 bean 在最终的环境中工作,还有很多工作要做。要了解部署描述符信息,如何使用属性页,模块的组装和应用的部署等信息,请参照第八章

关于使用已完成的企业 bean 的建议,参照附录A


更多的阅读

企业bean是你的应用中强大而灵活的部分。创建企业bean的基础部分是很简单的,特别是借助Sun Java System Studio IDE类似的工具。然而,要使它们满足你的应用的需要是非常复杂的。要了解详细的信息请参照http://java.sun.com/products/ejb/docs.html中的Enterprise JavaBean Specification, version 2.0

创建EJB组件

816-7864-10

Table of Contents Table of Contents Next Chapter

 

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