EJB 入门

 转帖自  http://www.jspcn.net/htmlnews/11049308165311854.html

 

服务框架

   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传递时,容器开发商通过包装编码插入其自?  

 

会话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传递参数意味着在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的内外存交换

  现在应该来看看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文件中。  

 

自管理的事务

   如果声明一个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必须请求这个事务的上下文。

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值