您的位置:寻梦网首页编程乐园Java天地EJBEnterprise JavaBeans 导论
Java 天地
Enterprise JavaBeans 导论
导论 1 导论 2 导论 3 导论 4 导论 5 导论 6

Enterprise JavaBeans导论六

自管理的事务

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

(作者:黄冬  来源:www.chinacode.net)