Enterprise JavaBeans导论

来源:百度文库 编辑:神马文学网 时间:2024/04/27 17:00:53

Enterprise JavaBeans导论
作者:黄冬 中国代码同盟消息组
http://www.360doc.com/UserHome/720362
 
服务框架

   EJB并不是一个产品。它是Java服务器端服务框架的规范,软件厂商根据它来实现EJB服务器。利用程序开发者可以专注于支撑利用所需的贸易逻辑,而不用担心四周框架的实现标题。
EJB规范具体地解释了一些最小但是必须的服务,如事务,安全和名字等。软件厂商根据这些规范请求以保证一个enterprise bean能应用某个必须的服务。规范并没有阐明厂商如何实现这些服务。这使得通过浏览规范来学习EJB更加艰苦,由于它答应厂商在不就义核心服务的可移植性的条件下来供给一些加强功效。

JavaBeans和Enterprise JavaBeans

  JavaBeans是Java的组件模型。在JavaBeans规范中定义了事件和属性等特点。Enterprise JavaBeans也定义了一个Java组件模型,但是Enterprise JavaBeans组件模型和JavaBeans组件模型是不同的。 JavaBeans重点是答应开发者在开发工具中可视化的把持组件。JavaBeans规范具体地解释了组件间事件登记、传递、辨认和属性应用、定制和持久化的利用编程接口和语意。 Enterprise JavaBeans的着重点是具体地定义了一个可以portably地安排Java组件的服务框架模型。因此,其中并没提及事件,由于enterprise bean通常不发送和吸收事件。同样也没有提及属性------属性定制并不是在开发时进行,而是在运行时(实际上在安排时)通过一个安排描写符来描写。
  不要寻找JavaBeans和Enterprise JavaBeans之间的类似性。他们都是组件模型规范,但是前者阐明了开发工具中利用程序组装的标题,而后者则着重于安排组件的服务框架的细节。不要毛病地认为JavaBeans是用于客户真个开发,Enterprise JavaBeans是用于服务器真个开发。JavaBeans也可作为进行非图形化服务器端Java利用开发的组件模型。差别是当你应用JavaBeans创立服务器利用时,你还得设计全部的服务框架。用Enterprise Javabeans框架是现成的,你只需遵守它的APIs.对于复杂的服务器端利用程序,显然应用Enterprise JavaBeans比重新开发更简略。

Enterprise JavaBeans系统结构

  EJB服务器是治理EJB容器的高端过程或利用程序,并供给对系统服务的访问。EJB服务器也可以供给厂商自己的特征,如优化的数据库访问接口,对其他服务(如CORBA服务)的访问,对SSL 3.0的支撑等。一个EJB服务器必须供给对可访问JNDI的名字服务和事务服务支撑。一些可能的EJB服务器的例子如:

数据库服务器
利用服务器
中间件服务器


  EJB容器是一个治理一个或多个EJB类/实例的抽象。它通过规范中定义的接口使EJB类访问所需的服务。容器厂商也可以在容器或服务器中供给额外服务的接口。

  现在没有EJB服务器和EJB容器间接口的规范。由于目前容器通常由EJB服务器来供给,所以一旦接口标准化了,厂商就可能供给可以在任何兼容的EJB服务器上运行的容器。

  Home接口列出了所有定位、创立、删除EJB 类实例的方法。Home对象是home接口的实现。EJB类开发者必须定义home接口。容器厂商应当供给从home接口中产生home对象实现的方法。

  远程接口(remote interface)列出了EJB类中的贸易方法。EJBObject实现远程接口,并且客户端通过它访问EJB实例的贸易方法。EJB类开发者定义远程接口,容器开发商供给产生相应的EJBObject的方法。客户端不能得到EJB实例的引用,只能得到它的EJBObject实例的引用。当客户端调用一个方法,EJBObject吸收恳求并把它传给EJB实例,同时供给过程中必要的包装功效。客户端利用程序通过home对象来定位、创立、删除EJB类的实例,通过EJBObject来调用实例中的贸易方法。客户端可以用Java来编程,通过Java RMI来访问访问home对象和EJBObject,或用其他语言编程并通过CORBA/IIOP访问,使得安排的服务器端组件可以通过CORBA接口来访问。


   上图是Enterprise JavaBeans系统结构的一个描写。下一节具体讨论了每个组件的细节。

**************

懂得EJB组件

  你现在应当已经熟悉了全部EJB系统结构及其重要的部件。这一节更具体地描写了这些部件,并解释它们运行时的行动语意。 Home接口  EJB库应用enterprise bean的客户端通过它的home接口创立它的实例。Home接口包含一或多个用来创立enterprise bean实例的create()方法。这个home接口不是由bean来现,而是通过称为home object的类来实现。一个home object的实例在服务器中实例,使得客户端可以访问它们。

  定位home object 一个home object的引用被放在名字服务中,客户端能通过JNDI访问它。EJB服务器一般供给某种名字空间的实现,固然有时可以应用外部的名字空间。在这两种情况下客户端都必须知道名字空间的地位以及JNDI的高低文类。例如,一个客户真个applet可能吸收名字空间和JNDI高低文类作为applet的参数。除了供给地位和类名,客户端也必须知道在名字树中定位home object.这些必须在客户端启动时供给。当安排者把enterprise bean安排进EJB服务器中时,他必须可以以参数情势指定名字树,如ejb/accounting/AccountsPayable.客户端必须获得这个完整的路径名来定位并获得AccountsPayable home object的引用。并不是说客户端通过JNDI获得容器。客户端应用JNDI查找home接口的实现。Home接口的实现由某个特别的container来供给,但这是该容器厂商的细节,enterprise bean开发者和客户端应当疏忽它。

Home接口中的方法

  Enterprise bean开发者定义ejbCreate()方法的同时必须在home接口中声明与其相应的create()方法。实体bean可以包含finder方法以使得客户端能定位已有的实体bean.

  Home接口是通过持续javax.ejb.EJBHome来定义的。该接口包含如下的方法:
public interface javax.ejb.EJBHome extends Remote {
public EJBMetaData getEJBMetaData() throws RemoteException;
public void remove(Handle handle) throws
RemoteException,RemoveException;
public void remove(Object primaryKey) throws
RemoteException,RemoveException;

  一个bean的home接口可以象下面这样:
public interface myHome extends EJBHome {
public myRem create() throws RemoteException,CreateException;
public myRem create(String str) throws
RemoteException,CreateException;

  其中public interface myRem extends EJBObject { … }容器开发商负责供给实现home接口的home对象,由于只有开发商才干实现存贮enterprise bean的库的编码。容器定义容器在懂得EJB规范时容器这个术语并不应从字面上简略地懂得为类,而是一层代替bean履行相应服务的接口。容器开发商供给运行在EJB服务器中一套完成这些功效的工具和接口。

  这些服务包含:?与二级存储中交换(对会话bean)
持久性治理(对实体bean)
实现创立和查找服务的home object的可用性
在可通过JNDI访问的名字空间home object的可视性
正确的创立、初始化和删除bean
保证贸易方法正确地运行在事务高低文中
实现某一基础的安全服务
从home object和EJBObject上的RMI上产生stub和skeleton

  容器和EJBObject对规范经常引用由容器或EJBObject供给的服务。这些服务只是用来阐明而不暗示特别类的服务需求。支撑enterprise bean的EJBObject和容器类都由容器开发商供给。这些类必须完成bean容器的功效。对bean来说容器和EJBObject是不同的进口点,对某个特别的服务供给支撑奇特的能力。例如,容器通过读安排描写符来知道利用于bean方法的事务属性。然而,这些贸易方法通过EJBObject调用。EJBObject必须与容器通信来断定调用贸易方法的事务高低文。断定以后,EJBObject在调用贸易方法以前建立事务高低文。重要的是EJBObject 和容器的协同工作来实现容器所需的事务。容器厂商供给二者的实现,但对二者的功效分割却是自由的。与home接口的关系

  目前厂商供给工具来读home 接口并产生作为容器的home object.在这种情况下厂商对每个enterprise bean类应用不同的容器类。容器厂商可以应用其它的实现策略,如一个容器类实现多个home接口,甚至一个标准的容器类创立独立的home object实现。唯一的必要条件是容器厂商必须使客户端能通过JNDI访问home object. 客户端和bean开发者都不需关心容器和home object的实现细节。Enterprise JavaBeanEnterprise bean是开发者编写的供给给用程序功效的类。开发者可以选择创立会话bean或实体bean,通过实现不同的接口声明其安排描写符来加以区分。

对于会话bean:
public class myBean implements javax.ejb.SessionBean …
对于实体bean:
public class myBean implements javax.ejb.EntityBean …

  客户端不会直接访问enterprise bean中的任何方法。客户端通过EJBObject 间接调用bean中的方法,EJBObject就象一个代理一样。在把调用通过EJBObject传递时,容器开发商通过包装编码插进其自己的功效,这称为方法插进。方法插进的一个例子是为每个方法调用创立一个新的事务高低文,当方法返回到EJBObject时提交或回滚事务。当容器厂商的工具在安装bean产生stub和skeleton时,它产生bean的EJBObject一个stub和skeleton.实际上它并不创立bean本身的stub和skeleton,由于bean不会通过网络被访问。EJBObject是真正的网络对象。Bean 是包含利用相干的贸易编码的代表。容器也可以调用bean中的某个方法。例如,容器保证当一个bean 实例天生后,homeobject中的create()的任何参数会传递bean相应的ejbCreate()方法。 Enterprisebean还有其它的接口和请求。然而,会话bean和实体bean的请求是不同的。这些在随后详述会话和实体bean的章节中会cover.Remote Interface编写完enterprise bean后开发者创立了一个客户端可访问创立方法的home interface,在home interface中每一个create()方法在相应的bean中都必须有一个ejbcreate()方法。同样,开发者必须创立描写客户端能够访问的贸易方法的remoteinterface。由于所有的客户端调用都通过EJBObject,因此实现这个接口的是EJBObject而不是homeobject. Remote interface中列出的方法名和signature必须和实现bean的方法名和signature雷同。这不同于home interface--方法signature是一样的,而名字却不同。

  以下是一个remote interface的例子:
public interface Account extends javax.ejb.EJBObject {
public void deposit(double amount) throws RemoteException;
public void withdraw(double amount) throws RemoteException;
public double balance() throws RemoteException;
}
  所有声明的方法都必须抛出一个RemoteException例外,由于规范请求客户端stub是RMI兼容的。但这并不意味着排除了用其它的传输方法的stub/skeleton实现,如CORBA/IIOP.Remote interface持续javax.ejb.EJBObject接口,增长了额外的方法请求。EJBObjectEJBObject是网络上可视的对象,包含stub和skeleton,作为bean的代理。Bean的remoteinterface持续了EJBObject接口,而EJBObject类实现这个remote interface,使得bean类有自己的EJBObject类。对每个bean类有一个定制的EJBObject类。

  如下是EJBObject接口的定义,被bean的remote interface持续:
public interface javax.ejb.EJBObject extends java.rmi.Remote {
public EJBHome getEJBHome() throws RemoteException;
public Object getPrimaryKey() throws RemoteException;
public Handle getHandle() throws RemoteException;
public void remove() throws RemoteException,RemoveException;
public boolean isIdentical(EJBObject other) throws RemoteException;
}
  实现这个接口的EJBObject类是一个RMI服务器对象,由于它是他实现了一个RMI remote interface.留心bean本身不是一个remote object,在网络上是不可视的。当容器实例 化了这个EJBObject类时,容器会初始化bean实例的引用,使得它能正确地delegate贸易方法调用。厂商的实现是掩护EJBObject实例和bean实例的一对一关系。由于remote interface包含EJBObject接口的方法,所以bean不必显式地实现这个接口,固然它供给了列出的贸易方法的实现。由于EJBObject必须正式地实现bean的remote interface,容器在bean安装时产生EJBObject的源代码,这些产生的源代码实现了bean的remote interface.范例 的EJBObject有一个奇特的类名,作为EJBObject类和bean的接洽。
****************

会话bean

  会话bean是一种通过home interface创立并对客户端连接专有的enterprise bean.会话bean实例一般不与其它客户端共享。这答应会话bean掩护客户真个状态。会话bean的一个例子是购货车,众多顾客可以同时购货,想他们自己的购货车中加东西,而不是向一个公共的购货车中加私人的货物。

  定义一个会话bean可以通过定义一个实现javax.ejb.SessionBean接口的类来创立一个会话bean.该接口定义如下:
public interface javax.ejb.SessionBean extends javax.ejb.EnterpriseBean {
public void ejbActivate() throws RemoteException;
public void ejbPassivate() throws RemoteException;
public void ejbRemove() throws RemoteException;
public void setSessionContext(SessionContext context)
throws RemoteException;
}
javax.ejb.EnterpriseBean是一个空接口,是会话bean和实体bean的超类。

会话bean的交换

  容器开发商可以实现把会话bean的实例从主存移到二级存储中的交换机制,这可以增长一段时间内实例化的会话bean的总数。容器掩护一个bean的时间期限,当某个bean的不运动状态时间达到这个阙值,容器就把这个bean拷贝到二级存储中并从主存中删除。容器可以应用任何机制来实现bean的持久性存储。最常用的方法是通过bean的串行化。Bean开发者在bean中应避免应用transient fields。EjbActivate()和ejbPassivate()用来掩护这个fields值。

活化和钝化

  为了支撑厂商供给会话bean的交换,规范定义了钝化--把bean从主存转移到二级存储的过程,活化--把bean恢复到主存中往的过程。在SessionBean接口中声明的EjbActivate()和ejbPassivate()方法,答应容器通知已经被活化的bean它将要被钝化。Bean开发者可以用这些方法开释和恢复处于钝化状态的bean所占领的值、引用和系统资源。一个可能的例子是数据库连接,作为有限的系统资源,不能被钝化的bean使
用。

  有了这些方法就使得不必在应用transient.事实上,应用transient可能是不安全的,由于串行化机制主动地把值设为null或0。而通过ejbActivate()和ejbPassivate()方法显式地设置这些fields更好一些。依附Java的串性化机制把transient fields设成null也是不可移植的,由于当bean安排在不应用Java的串性化机制获得持久性的EJB容器中时该行动会产生转变。假如容器不供给交换,那么这些方法将永远不会被调用。 当客户端调用bean的贸易方法时钝化的bean被激活。当EJBObject收到方法调用的恳求时,它通知容器需要活化的bean.当活化完成时,EJBObject代理对bean的方法调用。 假如bean 参与一个事务,那么它不能被钝化。把bean放在主存中更有效率,由于事务通常在很短的时间内完成。假如bean没有钝化前必须开释或活化前必须重置的状态,那么这些方法可置空。在大多数情况下,bean开发者不必在这些方法中做任何事。

会话bean的状态治理

  会话bean的安排描写符必须声明该bean是有状态或无状态的。一个无状态bean是在方法调用间不掩护任何状态信息的bean。通常,会话bean的优点是代替客户端掩护状态。然而,让会话bean无状态也有一个利益。无状态bean不能被钝化。由于它不掩护状态,所以没有需要保存的信息。容器可以删除bean的实例。客户端永远不会知道无状态bean的删除过程。客户真个引用是EJBObject.假如客户端稍后又调用了一个贸易方法,则EJBObject通知容器在实例化一个新的会话bean.由于没有状态,因此也没有信息需要恢复。

  无状态bean可以在客户端间共享,只是在某一时刻只能有一个客户端履行一个方法。由于在方法调用间没有需要掩护的状态,所以客户端可应用任何无状态bean的实例。这使 得容器可以掩护一个较小的可服用bean的缓冲池,节俭主存。由于无状态bean在方法调 用间不能掩护状态,因此从技巧上讲在home interface的create()方法不应有参数。在任何无状态bean的实例。这使 得容器可以掩护一个较小的可服用bean的缓冲池,节俭主存。由于无状态bean在方法调 用间不能掩护状态,因此从技巧上讲在home interface的create()方法不应有参数。在创立时向bean传递参数意味着在ejbCreate()返回时需要掩护bean的状态。而且,经过EJBObject调用贸易方法的成果使得容器必须能重创立一个无状态的bean.这时在开端创立bean时的参数就不存在了。厂商的安装工具应当能检查home interface的无状态对话bean以保证其不包含带参数的create()方法。

实体bean

实体bean的角色

  实体bean用来代表底层的对象。最常用的是用实体bean代表关系库中的数据。一个简略的实体bean可以定义成代表数据库表的一个记载,也就是每一个实例代表一个特别的记载。更复杂的实体bean可以代表数据库表间关联视图。在实体bean中还可以考虑包含厂商的加强功效,如对象--关系映射的集成。

  通常用实体类代表一个数据库表比代表多个相干联的表更简略且更有效。反过来可以轻易地向实体类的定义中增长关联,这样可以最大地复用cache并减小旧数据的表现。

实体bean和对话bean的比较

  看起来会话bean好象没什么用处,尤其对于数据驱动的利用程序。当然事实并不是这样。由于实体bean(譬如说)代表底层数据库的一行,则实体bean实例和数据库记载间就是一对一的关系。由于多个客户端必须访问底层记载,这意味着,不同于会话bean,客户端必须共享实体bean。由于是共享的,所以实体bean不答应保存每个客户真个信息。会话bean答应保存客户真个状态信息,客户端和会话bean实例间是一对一的。实体bean答应保存记载的信息,实体bean实例和记载间是一对一的。一个理想的情况是客户端通过会话bean连接服务器,然后会话bean通过实体bean访问数据库。这使得既可以保存客户真个信息又可以保存数据库记载的信息。



  同时会话bean也不能供给在雷同或不同的EJB类调用间进行全局的事务把持。没有会话bean,利用程序开发者(客户端开发者)就必须懂得EJB类的事务请求,并应用客户真个事务划分来供给事务把持。EJB的重要利益就是利用开发者不需知道EJB类的事务需求。一个会话bean可以代表一个贸易把持,进行事务把持,不需要客户端进行事务划分。

Finder方法

  通过home或remote interface创立和删除bean的实例,对实体bean和会话bean来说有不同的含义。对会话bean来说,删除意味着从容器中删除,不能再应用,并且其状态信息也丧失了。对于实体bean,删除意味着底层数据库记载被删除了。因此,一般不把删除作为实体bean生命周期的一部分。

  创立一个实体bean意味着一个记载被插进数据库中。与删除把持类似,创立把持通常也不作为实体bean生命周期的一部分。客户端访问实体bean需要先找到它。除了create()方法,一个实体bean的home interface还有finder方法。客户端需要根据利用程序的限制来辨认一个特别的数据库记载。例如:



public interface AccountHome extends EJBHome {
public Account findByFirstLast(String first, String last)
throws RemoteException,FinderException;
public Account findByAccountNumber(String acctNum)
throws RemoteException,FinderException;
}

  当客户端调用home object的任何方法,容器把调用传递到实体bean的相应方法中。

Public class myEntityBean implements EntityBean {

public Obejct ejbFindByFirstLast(String first, String last) {
//runs appropriate singleton SELECT statement
//returns primary key for selected row
}
public Obejct ejbFindByAccountNumber(String acctNum) {
//runs appropriate singleton SELECT statement
//returns primary key for selected row
}
}


  一个较好的方法是把finder方法当成数据库的SELECT语句,而动态SQL参数相当于方法的参数。留心home interface中的finder方法向客户端返回一个对EJBObject的远程引用。Bean中的Finder方法向容器返回一个唯一的标识符,称为主键。容器用这个主键实例化一个代表选定的记载的EJBObject。不论如何实现finder方法,容器都用这个主键代表这个选定的记载,由实体类来决定如何用唯一的标识符来代表记载。

  由可能一个finder方法得到满足SELECT语句条件的多个记载。这种情况下bean的finder方法返回一个主键的枚举类型。Home interface的Finder方法定义成向客户端返回EJBObject引用的枚举类型。

Public interface AccountHome extends EJBHome {

public Enumeration findByCompany(String companyName)
throws RemoteException,FinderException;
}
public class myEntityBean implements EntityBean {

public Enumeration ejbFindByCompany(String companyName) {
//runs appropriate SELECT statement
//returns an Enumeration of primary keys
}
}

主键

  主键这个词有可能被曲解。把它懂得为唯一的标识符更适当些。当实体bean代表一个数据库记载时,主键可能是该记载的组合键。对于每个实体bean的实例,有一个相应的EJBObject.当一个EJBObject与一个实体bean实例对应时,该实例的主键保存在EJBObject中。

  这时说该实体bean的实例有一个标识符。当客户端调用home object的finder方法时,容器会用没有标识符的实体bean的实例来履行这个恳求。容器可能为此保持一个甚至多个匿名的实例。不论如何实现finder方法,都必须向容器返回底层数据的主键,如数据库的记载。假如多个记载满足条件,那么就返回多个主键。当容器得到主键后,它会用该主键初始化一个EJBObject.容器也可以初始化一个与每个EJBObject关联的实体bean的实例。由于底层记载的标识符在EJBObject中保存,因此在bean实例中没有状态。因此,容器可以在EJBObject上调用贸易方法时再实例化bean,以节俭内存资源。

  当finder方法向容器返回主键时,容器首先会检查该主键的EJBObject是否已经存在。假如该主键的EJBObject已经存在,那么容器不会创立一个新的EJBObject,而是向客户端返回这个已存在的EJBObject的引用。这样就保证了每个记载只有一个EJBObject的实例,所有的客户端共享EJBObject.

  主键只是在该类中唯一地标识bean的实例,容器负责保证其领域。应当明确finder方法只是从数据库中取出数据的主键,而不包含其它的数据项。也可能调用finder方法后不产生任何实体bean的实例,只产生包含该主键的EJBObject,当客户端调用EJBObject的方法时在产生并导进实体bean的实例。 Home object保证客户端可以访问以下方法:
public myRem findByPrimaryKey(Obejct key) throws …;
EJBObject供给以下方法的一个实现:
Public Object getPrimaryKey();

  客户端能在任何时候获得实体bean的主键,并且以后可以应用该主键通过home interface重建对实体的引用。主键类的类型在安排描写符中指定。Bean开发者可以用任何类类型来表现主键。唯一的请求是类必须实现serializable,由于主键可能在客户和服务器间传递。

*********

实体bean的内外存交换

  现在应当来看看javax.ejb.EntityBean接口。
public interface javax.ejb.EntityBean extends EnterpriseBean {
public void ejbActivate() throws RemoteException;
public void ejbPassivate() throws RemoteException;
public void ejbRemove() throws RemoteException,RemoveException;
public void setEntityContext(EntityContext ctx) throws RemoteException;
public void unsetEntityContext() throws RemoteException;
public void ejbLoad() throws RemoteException;
public void ejbStore() throws RemoteException;
}

  活化和钝化的过程与会话bean类似。然而,不在事务中的实体bean是无状态的;其状态总是和底层的数据同步的。假如我们象钝化会话bean那样钝化实体bean,则当钝化无状态实体bean时只会删除它。但是由于容器调用finder方法需要匿名的实体bean,容器可 能为此把不运动的实体bean钝化到一个私有内存池中。一旦从EJBObject中删除实体 bean,则同时删除了标识符(主键关联)。 当客户端调用没有相干的实体bean的EJBObject的贸易方法时,容器就可能用这个内存池重新分配实体bean. 留心这个内存池中的bean没有标识,可以被任何EJBObject重用。容器可以可以不掩护任何有EJBObject的实体bean,除非有一个贸易方法在通过EJBObject被调用。假如实体bean在事务中则需保持其与EJBObject的关联。

自治理的持久性

  由于实体bean代表底层的数据,因此我们需要把数据从数据从数据库中取出然后放在bean中。当容器第一次把一个实体bean的实例与EJBObject关联时,它就开端了一个事务并调用了这个bean的ejbLoad()方法。在这个方法中开发者必须供给从数据库中取出正确的数据并把它放在bean中。当容器将要提交一个事务它首先调用bean的ejbStrore()方法。这个方法负责向数据库中回写数据。我们称之为自治理持久性,由于bean方法中的代码供给了这种同步。当ejbLoad()方法完成时,bean有可能与底层数据库不一致。贸易方法的调用触发了与EJBObject关联的bean的分配,然后在事务中履行的ejbLoad()必须在安排描写符中声明。根据吸收到的方法调用恳求,EJBObject和容器一起建立一个事务高低文。容器分配EJBObject的bean并调用bean的ejbLoad()方法。这个方法现在运行在事务高低文中。这个事务高低文传递给数据库,根据安排描写符中指定的孤立性级别,这个事务锁定数据库中被访问的数据。只要事务高低文运动,数据库中的数据就一直保持锁定状态。当客户端或容器提交事务时,容器首先调用bean的ejbStore()方法,把bean中的数据回写到数据库中。相应的数据库记载在ejbLoad()和ejbStore()间保持锁定保证了bean和数据库间的同步。其间可以进行不同的贸易方法调用。而且,ejbLoad()和ejbStore()明确地区分了事务边界,事务中可以进行任何贸易方法调用。事务的持续时间由安排描写符决定,也可能由客户端决定。留心不必应用ejbActivate()和ejbPassivate()方法来履行与数据库间的同步。

容器治理的持久性

  假如安排描写符声明bean应用容器治理的持久性,则不用ejbLoad()和ejbStore()来访问数据库。容器会把数据从数据库中导进到bean中,然后调用bean的ejbLoad()方法来完成从数据库中吸收数据。同样地,容器调用bean的ejbStore()方法来完成把数据回写到数据库中。这些方法实际 上没有履行任何数据库把持。当开发商用复杂的工具来供给容器治理持久性时,如主动产生能进行对象--关系映射的实体bean类,规范规定了厂商必须供给的容器治理实体持久性的最小需求集。安排描写符可以指定bean的一个public域来实现与数据库列简略映射。容器应用安排描写符读出bean的这个public域并写到相应的列,或从数据库列中读出数据写到public域中。容器治理的持久性对EJB开发者来说是非常好的服务,且不需对象--关系影射等其他复杂的机制,开发者会创造它比自治理的持久性更有效率。

安排描写符

区分EJB开发的角色

  EJB开发中两个重要的角色是bean开发者和bean安排者。有很多属性开发者不能预知,如数据库的网络地址,应用的数据库驱动程序等等。安排描写符作为由开发定义的特征表,由安排者添进正确的值。安排描写符有标准的格式,在开发和安排环境中是可移植的,甚至在不同EJB平台间也是可移植的。 Enterprise bean的行动把持除了为开发和安排的协同供给一个标准的属性单,安排描写符也应包含bean应如何履行有关事务和安全的细节信息。一些如访问把持链(ACL)等属性,也应当由安排者来调剂以保证适当的用户能在运行时应用bean.其它属性,如事务信息,有可能完整由开发者指定,由于一般由开发者创立数据库访问代码,并熟知bean的方法应如何运行事务。

定义安排描写符

  安排描写符是一个标准的Java类。创立一个实例,导进数据,然后串行化。这个串行化的安排描写符放在一个jar文件中并和enterprise bean类一起送到安排环境。安排者读取这个串行化的安排描写符,可能修正一些属性值,然后应用这个修正后的安排描写符来安装enterprise bean.

  下面是安排描写符的一部分内容。它是两个其它的安排描写符类的超类。实际上超类是描写这个bean的描写符。

Javax.ejb.deployment.DeploymentDescriptor
?bean home name
?bean class name
?home interface class name
?remote interface class name
?environment properties
?control descriptors
?access control list

DeploymentDescriptor有两个子类:

javax.ejb.deployment.SessionDescriptor
?state management type
?session timeout

javax.ejb.deployment.EntityDescriptor
?list of ocntainer-managed fields
?primary key class name

  描写符为实体bean和每个方法定义了事务和安全属性。这些对象的一个数组在
DeploymentDescriptor中指定。

javax.ejb.deployment.ControlDescriptor
?transaction isolation level
?Method object to which this descriptor applies
?run-as mode(for odentity mapping)
?run-as identity(for identity mapping)
?transaction attribute


  安排一个enterprise bean时,分配对应的描写符,然后初始化,串行化,再将其与enterprise bean类一起放进到一个jar文件中。不同厂商在定义安排描写符时可能有不同的方法。例如,一个厂商可能应用文本方法,而另一厂商可能供给图形工具。但最后成果的安排描写符是一个标准的格式,并且在不同平台间是可移植的。

EJB Jar文件

  为了包装一个enterprise bean,bean的类,接口和串行化的安排描写符放在一个jar文件中。这个jar文件必须有一个manifest文件的进口以声明一个enterprise bean的安排描写符。
Name:AccountDD.ser
Enterprise-Bean:true

  在manifest中作为enterprise bean列出的是安排描写符,而不是bean类。安排描写符除了定义enterprise bean,还供给jar文件中所有文件的完整描写。开发者不必关心EJB jar文件的创立。厂商应当供给一个工具来赞助开发者创立安排描写符,然后把所有必须的文件打包进一个jar文件中。

***********

3.事务

CORBA OTS

  EJB的事务模型与OTS类似。事实上,CORBA兼容的EJB服务器必须供给一个OTS兼容的事务服务。懂得OTS如何工作有助于懂得EJB中事务的工作方法。

定义事务

   一个事务正式地定义了一个原子工作单位。一个事务中可以包含多个把持,当事务终止时,所有履行的把持或者完整履行或者完整放弃。这称为提交和回滚。

  在数据库利用中广泛地应用事务。好的数据库产品对事务供给很强的支撑。一个事务中访问的记载在全部事务持续期间保持锁定状态。基于数据库产品在事务开端时可以选定不同的锁定状态。选定的锁定级别应在其它的事务中优化把持的并发访问时保证数据的完整性。网络上的事务可能是散布式的,例如客户端可能在单个事务中访问两个不同的数据库。为了支撑散布式事务,大多数事务治理器(包含数据库)支撑两段提交。在两段提交协议中,事务治理器在准备提交事务前首先询问所有的事务参与者的工作是否完成。这是协议的第一阶段。一旦每个参与者批准提交,第二阶段才会开端。事务治理器发送独立的提交把持的命令。

OTS中的要害部件

  为了更好地懂得OTS如何工作,我们需要先看看其要害部件。以下的部件可以直接地映射到EJB,而懂得这些部件如何在OTS中工作能使我们更好地懂得EJB中的事务。
Control
terminator
Coordinator
Resource
Synchronization

  下图示出了这些对象中定义的重要方法,以及在事务系统中这些对象的作用。



  虚线框内是一个事务。事务中所有的对象都参与了这个事务,提交和回滚对所有的Resource对象都实用。 Control对象代表一个事务。从该对象我们可以得到Coordinator和Terminator 。EJB开发者不会看到Control对象,容器代表bean用Control对象来治理事务。

  当一个bean方法返回且该方法在安排描写符中声明在方法返回前需提交该事务时,容器也用Terminator来提交或回滚事务。提交或回滚事务时,所有事务中的对象都会相应的提交或回滚。

  Resource是包含事务状态的对象。例如,它可能是一个数据库连接。在这个对象上调用commit()会更新数据库。一个rollback()调用会恢复该事务开端时通过这个连接对数据作的任何转变。完成提交或回滚后,数据库中相应的记载就会解锁。利用的锁级别会在安排描写符中指定。这个对象的完整的方法集会显示这些对象实现了两段提交协议,使得每一个对象都有权决定全部事务是提交还是回滚。当一个事务完成时,不论是提交还是回滚,都要通知Synchronization对象。与Resource不同,它并不参与两段提交协议,所以无权表决一个事务应当提交还是回滚。在事务中它扮演一个被动的角色。 Coordinator是使这一切工作起作用的对象。Resource和Synchronization通过该对象注册到事务中。Bean不直接访问这个对象。 Transaction-aware objects that are intended for use with EJB will transparently obtain a reference to the current transaction"s Coordinator to register itself.

事务和可恢复的对象

  在OTS中事务和可恢复的对象不同。这个差别与EJB有关。在CORBAservice? OTS规范中具体地定义了这些类型,简略地说,可恢复对象有commit()和rollback()方法,答应事务直接地把持它自己的状态和行动。一个事务对象没有这些方法,不能被事务影响。然而,事务对象有与其关联的事务,以使分配的可恢复对象(或Resources)与事务对象确当前事务相干联。一个enterprise bean是一个事务对象的好例子。容器代表bean掩护事务。任何bean分配的可恢复对象在容器的赞助下透明地放置在事务中。Bean没有commit()或rollback()方法,因此事务不能直接把持bean.让bean作为一个可恢复的Resource并没有什么意义,由于这使得bean开发者必须为每个bean添加额外的代码,而enterprise bean几乎没有内在状态应当影响一个外部的事务。让enterprise bean作为可恢复对象的治理者,让可恢复对象完成这个工作会更好。

  留心bean在容器试图提交或回滚之前可以有权表决回滚一个事务。在EJBContext中Bean可以用Coordinator中的rollback_only()方法作为setRollBackOnly()给事务设置标记,以使事务终止时间达到时恳求回滚。还可以通过SessionSynchronization接口通知一个bean有关一个事务的成果。

在安排描写符中指定事务把持

  bean的安排描写符包含一个ControlDescriptor对象的数组。每个ControlDescriptor描写了与方法关联的事务把持。 Bean开发者指定bean方法中的事务把持。安排者在对方法的事务相干行动没有过细懂得的情况下一般不应转变这些值。如下的六个事务把持是在ControlDescriptor类定义的整形常量。除了该类的方法,没有其它的APIs能访问它们。Bean本身不访问事务把持。 Bean本身的方法不能访问其事务属性。容器读取这些把持值来掩护bean的相应的事务行动。


?TX_NOT_SUPPORTED
?TX_SUPPORTS
?TX_REQUIRED
?TX_REQUIRES_NEW
?TX_MANDATORY
?TX_BEAN+NANAGED
  你可以通过厂商供给的创立安排描写符的工具来为bean设置合适的ControlDescriptor.
TX_NOT_SUPPORTED
  该方法不应运行在事务高低文中。假如在一个事务中履行线程,那么这个事务将挂起直到线程从方法中返回。
TX_SUPPORTS
  该方法不需要事务,运行该方法时线程可能有一个运动的事务。
TX_REQUIRED
  该方法必须运行在事务中。假如线程已经有一个事务,则这个线程答应进进此方法。假如线程没有事务,则容器代表线程启动一个答应线程进进的事务,当线程返回是终止事务。一般应提交事务。假如现成调用setRollbackOnly()方法,则容器相应地履行一个回滚。
TX-REQUIRED_NEW
  不论线程是否有一个事务,容器都会在方法调用期间创立一个事务。当线程返回时,容器提交或回滚这个事务。假如线程有一个进行中的事务,则新事务会挂起直到线程返回或方法的事务终止。
TX_MANDATORY
  当调用这个方法时线程必须已经在一个事务中。假如线程没有事务,则容器会抛出一个例外。
TX_BEAN_MANAGED
  这个与上述几个不同。这种方法表明容器不应在事务治理中起作用。

JTS-Java事务服务

  实际上JTS不是一个事务服务--只是底层服务供给者的一层接口。JTS非常简略,由一个接口和几个例外组成。从例外列表很轻易能看出它类似OTS,固然它也可以作为其它服务的接口。对于声明事务把持方法为自治理的bean,可以通过这个接口访问事务服务。厂商也可以用它来供给对客户端划分事务的支撑。


如下是UserTransaction接口的定义:

public interface javax.jts.UserTransaction {
public void begin() throws IllegalStateException;
public void commit() throws
TransactionRolledBackException,
HeuristicMixedException,
HeuristicRollbackException,
SecurityException,
IllegalStateException;
Public void rollback() throws
SecurityException,
IllegalStateException;
Public void setRollbackOnly() throws
IllegalStateException;
public void setTransactionTimeout(int seconds);
public int getStatus();
//STATUS_ACTIVE,STATUS_COMMITTING,
//STATUS_COMMITTED,STATUS_MARKED_ROLLBACK
//STATUS_NO_TRANSACTION,STATUS_PREPARED
//STATUS_PREPARING,STATUS_ROLLEDBACK
//STATUS_ROLLING_BACK,STATUS_UNKNOWN
}
*******************

自治理的事务

   假如声明一个bean的事务把持为TX_BEAN_MANAGED,则这个bean可以访问事务服务。当事务把持利用于单个的方法时这个把持只能利用于全部的bean. bean访问事务服务的能力不能只对某个方法起作用。因此一个方法声明事务把持为TX_BEAN_MANAGED,而另一个方法声明为其它不同的事务把持是毛病的。厂商的安装工具应当能检测到并报告这个毛病。Bean分辨通过初始化时setSessionContext()或setEntityContext()方法的参数 SessionContext或EntityContext来访问事务服务。这些接口都是EJBContext的子类。

  EJBContext的定义如下:

Public interface javax.ejb.EJBContext {
public Identity getCallerIdentity();
public boolean isCallerInRole(Identity other);
public EJBHome getEJBHome();
public Properties getEnvironment();
public UserTransaction getUserTransaction() throwsIllegalStateException;
public boolean getRollbackOnly();
public void set RollbackOnly();
}


  一旦bean获得了一个UserTransaction的引用,就可以用这个引用治理自己的事务。有状态的会话bean的方法可以创立一个事务,而且不用终止事务就可以返回。假如还有线程调用bean的方法,容器检测是否有bean创立的运动的事务,假如被调用的事务是同一个事务,容器会答应当线程重新进进这个bean.假如bean在事务中且履行不同事务高低文的线程试图进进bean,容器会阻塞这个线程直到bean的事务终止。假如线程试图进 进事务时bean不在事务中,线程会履行一个自己的事务,容器会挂起线程当前的事务以答应线程进进。一旦线程离开方法就会恢复线程以前的事务,容器不会终止任何方法创立的事务。

   对于无状态会话bean和实体bean,当事务运动时bean的方法不答应返回。容器会为此抛出一个例外。

Leaving a tranaction active across method calls is stateful,and is not allowed for stateless session beans.Fro similar reasons,entity beans are also not allowed to maintain an open transaction state across method calls when the bean has declared the TX_BEAN_MANAGED transaction control.


会话同步接口

  有状态和无状态的会话bean都可以访问数据库,并且参与一个事务。为了让bean在事务中履行它的任务,bean开发者可以实现在bean中实现 javax.ejb.SessionSynchronization接口。容器能主动检测这个接口,容器会应用这个接口中的方法以使bean得到事务的状态信息。实体bean不支撑这个接口。由于实体bean are implicitly transaction aware,所以容器应用不同的方法把持一个事务中的实体 bean.

  SessionSynchronization接口定义如下:

public interface javax.ejb.SessionSynchronization {
public void afterBegin() throws RemoteException;
public void beforeCompletion() throws RemoteException;
public void afterCompletion(boolean yn) throws RemoteException;
}

  实际上一个事务不属于一个特别的bean的实例。一个客户端或容器中履行的线程创立一个事务,在履行bean中的代码时履行该事务。假如一个有事务高低文的线程将要进进一个会话bean,容器首先调用它的afterBegin()方法。Bean可以记载所有的贸易方法运行在事务中,随后履行事务把持。假如一个把持的内部标记显示这个线程在事务外运行,则会拒尽履行事务把持的恳求。直到调用afterCompletion()方法,bean会持续认为贸易方法的调用都在事务中履行。Bean将推断性地扫除内部标记,以表现随后到来的事务恳求将被拒尽。

  假如一个事务高低文的线程试图进进一个已经是另一个事务的一部分的Bean时, .Container将封闭进口,直到前一个事务提交或回滚,并且afterCompletion()方法被调用,此时,答应Bean 恢复它的状态。Container负责供给这些行动。当Container创造它将要提交一个事务时,将在这个事务的所有的session Bean上调用beforeCompletion()方法。这就给Bean足够的机会来结束事务的把持,如在提交前将数据写进数据库。反之,当Container 创造,将要回滚一个事务撕,BeforeCompletion()方法将不会被调用,由于将一个将被回滚的事务所产生的数据写进数据库是没有意义 的。

  AfterCompletion()是在一个事务即将提交或回滚时被调用,来通知Bean事务把持的终极成果。Bean可以用这个信息来修正自己的内部状态,但它不能用这个信息来保持任何它将要保存的事务。尽管session Bean可以创立,提交和回滚它自己的事务,但通常不推荐这样做。

  SessionSynchronization接口不供给整合外部和内部事务的能力。假如一个session bean实现了这个接口,则意味着它在方法调用之间要保持事务的状态。特别地,这也暗示在afterBegin()和afterCompletion()调用之间bean是处于一个事务中。这样,假如一个bean实现了SessionSynchronization接口并且在装配符中声明是无状态的就是一个毛病。厂商供给的安装工具应当可以捕捉到并报告这个毛病。无状态的session bean可以加进一个事务,但它们不能实现这个接口。事务可以是TX_BEAN_MANAGED,或者container可以在方法进口和从方法的返回上来开端和提交这个事务。Container不可答应在一个现存的事务中有一个线程进进方法,由于无状态的Bean的方法将无法知道正在运行的线程是否正在一个事务中。

  解决这个标题的一个方法是使container挂起现存的事务,逼迫方法总是认为线程没有在一个事务性的高低文中运行。有状态的Bran可以不实现这个接口而参与事务。但是,装配符必需要认真地配置以使得商务方法总能在正确的事务状态中运行。Bean自己没有通过这个接口来获得自己的事务的状态的权利。

加进事务

  EJBContext接口在前面的一节中已经先容了。其中有两个方法:

public boolean getRollbackOnly();
public void setRoolbackOnly();

  这些方法可以有任何bean来应用,而不仅仅是那些声明了其事务把持为bean-managed的bean。事实上,那些处理自己的事务的bean将不会用到这些方法,由于这些方法不是用来和外界的事务治理器进行交换事务状态的。

  当一个bean调用了setRollBackOnly()方法时,它是在向事务治理器询问何时结束将要回滚确当前事务。它将给它所参与的事务的成果一个选票。这些方法还存在于UserTransaction接口中,但由于大多数的bean都不访问这个接口,这些方法必须直接地在EJBContext中供给给bean。留心这个方法并不引发回滚把持,它只是简略地设置标记,表现事务在结束时应当回滚。不象JavaBan属性设置方法,这个方法不以boolean值作为参数。这个方法是特地设计成这样,以使得一个bean不能够转变另一个bean的回滚恳求。一个bean也许希看应用getRoolBackOnly()方法,来检查当前的事务的状态。假如另一个bean已经标记这个事务为rollback,则正在调用的bean可以推测到并决定不能履行那些在、强制性达到把持,如数据库更新,而这些把持很有可能在事务结束时被反转过来。

客户划分的事务

  尽管一个JEB厂商所必须的,大服务器厂商也许决定供给一个类,使得用户可以直接访问事务治理器。当需要在同一个高低文中在两个不同的服务器上调用bean时,用户也许会希看这样做。当然,每个bean的装配符可以答应这样的行动。用户可以创立一个事务,然后在两个不同server上的两个不同的bean上调用商务方法,而将事务的高低文也作为调用的一部分进行传递。一旦调用结束,用户将推测地结束事务。有container厂商产生的stub和skeleton将支撑事务高低文的隐式传递。

  这里是一个可能的例子:

Current current = new Current();
Current.setServiceProvider(txMgrURL);
Current.create();
Current.begin();
Current.doSomeWork();
RemRef1.doSomeWork();
RemRef2.doMoreWork();
Current.commit();

数据库把持的事务治理

  bean当然希看应用JDBC来建立到数据库的连接,并在其上进行把持。但是,为了符合EJB这种container治理事务的模式,连接不能应用主动提交特征,并且不应当在连接上试图提交或回滚。

  Container的角色是决定在这个事务中履行的所有行动应当提交还是回滚。这里提这样一个标题很好:container如何看到并治理由bean方法内部创立的数据库连接。尽管在规范中没有明确地提到,EJB将只能应用JDBC驱动,而JDBC也正是用来和EJB配合应用的。在数据库连接的创立时,驱动程序透明地将连接注册到正在履行的线程确当前事务中。之后当container决定结束事务时,数据库连接将主动地结束它。用OTS的术语说,数据库连接是不可恢复的资源,有事务服务在container的协助下,隐式地治理。尽管可以在这种情况下应用非事务感知的JDBC Driver,但开发者必须明确任何在数据库连接上所做的把持都不属于bean的事务,开发者还必须确保在从方法返回之前结束数据库连接事务。试图应用SessionSynchronization接口来合并数据库连接事务和bean本身的事务是不可靠的,是不应当作的。

散布事务的支撑

  一个散布事务在下面的情况下是需要的:
  . 一个用户应用用户划分的在多个server上的多个bean中创立和调用方法的事务。
  . 一个在其他的server上调用其他EJB的方法的bean的方法。

   对于这些工作厂商必须为EJBObject天生stub和skeleton来隐式地获得当前事务的高低文,同时将其通过方法调用传到远程bean。当将商务方法调用委派给bean时,远程bean的EJBObject的skeleton必须恳求这个事务的高低文。