- 1 介绍
- 2 与其他 Java apis 的关系
- 3 Java Transaction API
- 4 应用服务器中的JTA支持
- 5 Java Transaction API Reference
- 6 Related documents
1 介绍
该文档是对 Java Transaction API (JTA) 的一个说明。JTA 制定了事务管理器(Transaction manager)和分布式事务里涉及到的几个模块之间协作的 Java 接口。 这几个模块分别是:应用(the application),资源管理器(the resource manager),应用服务器(application server)。
JTA 包由三部分组成:
- 允许事务应用程序划分事务边界的高级应用程序接口。
- 行业标准X/Open XA协议的Java映射,允许事务资源管理器参与由外部事务管理器控制的全局事务。
- 高级事务管理器接口,允许应用服务器控制由应用服务器管理的应用程序的事务边界界定。
1.1 背景
企业Java中间件中的分布式事务服务包括五个参与者:事务管理器(the transaction manager),应用服务器(the application server),资源管理器(the resource manager),应用程序(the application program),通信资源管理器(communication resource manager)。这些参与者都通过实现不同的事务api和功能集为分布式事务处理系统做出贡献。
- 事务管理器提供支持事务界定、事务资源管理、同步和事务上下文传播所需的服务和管理功能。
- 应用服务器(或TP监视器)提供了支持应用程序运行时环境所需的基础设施,其中包括事务状态管理。此类应用服务器的一个例子就是EJB服务器。
- 资源管理器(通过资源适配器)提供对资源的应用程序访问。资源管理器通过实现事务管理器用于通信事务关联、事务完成和恢复工作的事务资源接口来参与分布式事务。这种资源管理器的一个例子就是关系数据库服务器。
- 为在现代应用服务器环境中操作而开发的基于组件的事务应用程序依赖于应用服务器通过声明性事务属性设置提供事务管理支持。此类应用程序的一个例子是使用行业标准Enterprise javabean (EJB)组件体系结构开发的应用程序。此外,其他一些独立的Java客户端程序可能希望使用应用服务器或事务管理器提供的高级接口来控制它们的事务边界。
- 通信资源管理器(CRM)支持事务上下文传播和对传入和传出请求的事务服务的访问。JTA文件没有指定与通信相关的需求。有关事务管理器之间互操作性的更多细节,请参阅JTS规范。
从事务管理器的角度,事务服务的具体实现不需要暴露;只需要定义高级接口,就可以从事务服务的用户驱动事务界定、资源注册、同步和恢复过程。JTA的目的是定义事务管理器所需的本地Java接口,以支持Java企业分布式计算环境中的事务管理。在下面的图中,小半圆表示JTA规范。文档的第3章详细描述了规范的每个部分。
1.2 目标受众
本文是适用于以下项的实现者:
- JTS等事务管理器。
- 资源适配器,如JDBC驱动程序和JMS提供程序。
- 事务资源管理器,如RDBMS。
- 应用服务器,如EJB服务器。
- 使用JavaTM编程语言编写的高级事务应用程序。
2 与其他 Java apis 的关系
2.1 Enterprise JavaBeans
Enterprise javabean体系结构要求EJB容器通过实现javax.transaction.UserTransaction
来支持应用程序级事务界定。UserTransaction
接口用于EJB Bean实现者(用于TX_BEAN_MANAGED Bean)和希望在用Java编程语言编写的程序中显式划分事务边界的客户端程序员。
2.2 JDBC 2.0 Standard Extension API
JDBC 2.0扩展规范中包含的新特性之一是对分布式事务的支持。为JDBC驱动程序创建了两个新的JDBC接口,以支持使用Java事务API的XAResource接口的分布式事务。新的JDBC 2.0接口是javax.sql.XAConnection
和javax.sql.XADataSource
。
支持分布式事务的JDBC驱动程序实现了javax.transaction.xa.XAResource
, javax.sql.XAConnection interface
和javax.sql.XADataSource
三个接口。
2.3 Java Message Service
Java消息服务提供者可以使用Java事务API来支持分布式事务。支持XAResource接口的JMS 提供商能够作为资源管理器参与使用两阶段提交事务协议的分布式事务处理系统。特别的是,JMS提供者需要实现javax.transaction.xa.XAResource
,javax.jms.XAConnection
和 javax.jms.XASession
三个接口。
2.4 Java Transaction service
Java事务服务(JTS)是一种用于构建事务管理器的规范,它在高层支持JTA接口,在低层支持CORBA对象事务服务1.1规范的标准Java映射。JTS使用CORBA标准IIOP协议为服务器之间的事务传播提供事务互操作性。JTS适用于为企业中间件提供事务系统基础设施的供应商。
3 Java Transaction API
Java事务API由三个元素组成:高级应用程序事务界定接口、用于应用服务器的高级事务管理器接口和用于事务资源管理器的X/Open XA协议的标准Java映射。本章详细说明了这些元素。
3.1 UserTransaction 接口
javax.transaction.UserTransaction
接口为应用程序提供了以编程方式控制事务边界的能力。此接口可由Java客户端程序或EJB bean使用。
UserTransaction.begin
方法启动一个全局事务,并将该事务与调用线程关联。事务管理器透明地管理事务到线程的关联。
JTA 对嵌套事务不做要求。当调用线程已经与事务关联,且事务管理器实现不支持嵌套事务时,执行UserTransaction.begin
方法将抛出NotSupportedException异常。
应用程序之间的事务上下文传播由客户端和服务器机器上的底层事务管理器实现提供。用于传播的事务上下文格式依赖于协议,必须在客户端和服务器主机之间协商。例如,如果事务管理器是JTS规范的实现,它将使用CORBA OTS 1.1规范中指定的事务上下文传播格式。事务传播对应用程序是透明的。
3.1.1 UserTransaction 对 EJB Service 的支持
EJB服务器需要支持UserTransaction
接口,以便EJB bean使用TX_BEAN_MANAGED事务属性。UserTransaction
通过使用EJBContext
接口的getUserTransaction
方法暴露给EJB组件。因此,EJB应用程序不直接与事务管理器接口进行事务界定;而是,EJB bean依赖EJB服务器为其所有事务工作提供支持,这些事务工作在Enterprise JavaBeans规范[5]中定义。
下面的代码示例演示了 TX_BEAN_MANAGED EJB session bean 对 UserTransaction 的使用:
// In the session bean’s setSessionContext method,
// store the bean context in an instance variable
SessionContext ctx = sessionContext;
..
// somewhere else in the bean’s business logic
UserTransaction utx = ctx.getUserTransaction();
// start a transaction
utx.begin();
.. do work
// commit the work
utx.commit();
3.1.2 UserTransaction 对事务性客户端的支持
Java客户端程序可以通过应用服务器的支持或客户端主机上的事务管理器的支持来使用UserTransaction
接口。
应用服务器的厂商被期望为管理员提供工具,以便在JNDI名称空间中配置UserTransaction
对象绑定。UserTransaction
对象的实现必须同时实现javax.naming.Referenceable
和java.io.Serializable
,以便对象可以存储在所有JNDI命名上下文中。
如果应用服务器支持事务客户端执行的事务界定,则应用服务器必须支持客户端程序使用JNDI查找机制获取UserTransaction
对象的引用。由于JTA没有为UserTransaction
定义JNDI名称,客户端程序应该使用适当的配置机制将名称字符串传递给JNDI查找方法。
这种实现的一个例子是通过使用系统属性。由以下代码进行说明:
// get the system property value configured by administrator
String utxPropVal = System.getProperty(“jta.UserTransaction”);
// use JNDI to locate the UserTransaction object
Context ctx = new InitialContext();
UserTransaction utx = (UserTransaction)ctx.lookup(utxPropVal);
// start transaction work..
utx.begin();
.. do work
utx.commit();
3.2 TransactionManager 接口
javax.transaction.TransactionManager
接口允许应用服务器代表被管理的应用程序控制事务边界。例如,EJB容器管理事务EJB组件的事务状态;容器主要使用TransactionManager
接口来划分事务边界,其中操作会影响调用线程的事务上下文。事务管理器将与线程的事务上下文关联作为其内部数据结构的一部分来维护。线程的事务上下文要么为null
,要么引用特定的全局事务。多个线程可以并发地与同一个全局事务关联。
对嵌套事务不做要求。
每个事务上下文都由Transaction
对象封装,Transaction
对象可用于执行特定于目标事务的操作,而不管调用线程的事务上下文是什么。下面的部分提供了更多的细节。
3.2.1 开始一个事务
TransactionManager.begin
方法启动一个全局事务,并将事务上下文与调用线程关联。
如果事务管理器实现不支持嵌套事务,则使用TransactionManager.begin
方法在调用线程已经与事务关联时抛出NotSupportedException
异常。
TransactionManager.getTransaction
方法返回表示当前与调用线程关联的事务上下文的Transaction
对象。此Transaction
对象可用于对目标事务执行各种操作。Transaction
对象操作的示例包括资源注册和同步注册。Transaction
接口在下面的3.3节中描述。
3.2.2 完成(completing)一个事务
TransactionManager.commit
方法完成当前与调用线程关联的事务。在commit
方法返回之后,调用线程不与事务关联。如果在线程不与任何事务上下文关联时调用commit
方法,TM将抛出异常。在某些实现中,提交操作仅限于事务发起者。如果不允许调用线程提交事务,TM将抛出异常。
TransactionManager.rollback
回滚与当前线程关联的事务。rollback
方法完成后,线程将不与任何事务关联。
3.2.3 挂起和恢复一个事务
调用TransactionManager.suspend
方法临时挂起当前与调用线程关联的事务。如果线程不与任何事务关联,则返回null
;否则返回一个有效的Transaction
对象。稍后可以将Transaction
对象传递给resume
方法,以恢复与调用线程的事务上下文关联。
TransactionManager.resume
方法将指定的事务上下文与调用线程重新关联。如果指定的事务是有效的事务,则事务上下文与调用线程相关联;否则,线程不与任何事务关联。
Transaction tobj = TransactionManager.suspend();
..
TransactionManager.resume(tobj);
如果调用线程已经与另一个事务关联,调用TransactionManager.resume
,事务管理器将抛出IllegalStateException异常。
注意,一些事务管理器实现允许挂起的事务由不同的线程恢复。在 JTA 中不对这个特性做要求。
应用服务器负责确保应用程序使用的资源已正确地从挂起的事务中删除。资源删除操作触发事务管理器,通知资源管理器将事务与指定的资源对象(XAResource.end(TMSUSPEND))分离。
当应用程序的事务上下文恢复时,应用服务器确保应用程序使用的资源再次与事务一起登记(enlisted)。由于恢复事务而注册资源将触发事务管理器通知资源管理器将资源对象与恢复的事务重新关联(XAResource.start(TMRESUME))。有关资源登记和事务关联的详细信息,请参阅第3.3.1和3.4.4节。
在EJB环境中,EJB服务器通常管理应用程序使用的事务资源(EJB bean的资源请求在bean的实例上下文中被跟踪和维护)。当暂停当前与EJB实例关联的事务时,应用服务器将检查bean实例使用的资源列表,以确定是否需要删除任何资源。对于当前与挂起事务一起征募(enlisted)的每个资源,应用服务器调用Transaction.delistResource
方法来将资源与事务分离。当EJB实例恢复事务时,应用服务器将检查正在使用的资源列表,并在将控制权交给bean的业务方法之前向事务管理器登记资源。有关应用服务器中JTA支持的进一步讨论,请参阅第4章。
3.3 Transaction 接口
Transaction
接口允许对与目标对象关联的事务执行操作。创建事务时,每个全局事务都与一个Transaction
对象关联。Transaction
对象可用于:
- 征募应用程序正在使用的事务资源。
- 事务同步回调的注册。
- 提交或者回滚一个事务。
- 获取事务的状态。
下面几节将描述这些功能。
3.3.1 资源征用(Resource Enlistment)
应用服务器提供应用程序运行时基础设施,其中包括事务资源管理。数据库连接等事务性资源通常由应用服务器与一些资源适配器一起管理,还可以选择使用连接池优化。为了让外部事务管理器可以协调资源管理器执行的事务工作,应用服务器必须征用并删除事务中使用的资源。
应用服务器执行的资源征用(enlistment)有两个目的:
- 通知事务管理器,它将作为资源管理器实例参与到全局事务。事务管理器通知参与事务的资源管理器 即 对应 connection (resource) 对象执行的操作。
- 它使事务管理器能够对每个事务使用的资源类型进行分组。按照X/Open XA规范的定义,资源分组允许事务管理器在 TM 和 RMs 之间执行两阶段提交事务协议。
对于应用程序使用的每个资源,应用服务器都会调用enlistResource
方法并指定一个XAResource
对象来标识这个资源。
enlistResource
请求将导致事务管理器通知资源管理器,通过调用XARsource.start
方法,开始将事务与通过相应资源执行的工作关联起来。事务管理器负责在其XARsource.start
方法调用中将适当的标志传递给资源管理器。XAResource接口在第3.4节中进行了描述。
如果目标事务已经有另一个XAResource对象参与事务,事务管理器将调用XARsource.isSameRM
方法来判断指定的XAResource是否表示相同的资源管理器实例。此信息允许TM对代表事务执行工作的资源管理器进行分组。
- 如果XAResource对象标识一个之前就已经在全局事务的资源管理器,TM 将新注册的资源与前一个 XAResource 对象分组,并确保相同的 RM 只接收一组prepare-commit调用,以完成目标全局事务。
- 如果XAResource对象标识一个之前没有参与全局事务的资源管理器,TM 将建立一个不同的事务分支 ID,并确保这个新的资源管理器通过适当的prepare-commit调用获知事务完成情况。
isSameRM
方法在3.4.9中有所描述。
事务分支在X/Open XA规范[1]中定义如下:“一个全局事务有一个或多个事务分支。分支是支持全局事务的工作的一部分,TM和RM在全局事务中使用独立但协调的事务保证(commitment)协议。RM支持全局事务的每个内部工作单元都是一个分支的一部分。在TM开始事务保证协议之后,RM不会在该事务分支上接收任何额外的工作。
RM可以从不同的分支接收代表相同事务的额外工作。不同的分支是相关的,因为它们必须以原子方式完成。TM提供给RM的每个事务分支标识符(或XID)都标识一个全局事务和一个特定的分支。RM可以使用这些信息来优化共享资源和锁的使用。”
Transaction.delistResource
方法用于将指定的资源与目标对象中的事务上下文分离。应用服务器使用以下两个参数调用delistResource
方法:
- 标识资源的XAResource对象。
- 一个标识是否是由于以下原因释放资源(delistment)的标记:
- 事务被挂起(TMSUSPEND)。
- 部分任务失败(TMFAILL)。
- 应用的正常资源释放(TMSUCCESS)。
TMFAIL的例子:应用程序在其连接操作上接收到异常。
释放(delist)请求将导致事务管理器通知资源管理器结束事务与目标XAResource的关联。标志值允许应用服务器指示它是否打算返回到相同的资源。事务管理器将其XAResource.end
方法调用中的适当标志值传递给相关的资源管理器。
3.3.2 事务同步(Transaction synchronization)
事务同步允许应用服务器在事务完成之前和之后从事务管理器获得通知。对于每个启动的事务,应用服务器可以选择注册一个javax.transaction.Synchronization
回调对象,由事务管理器调用:
- 在两阶段事务提交(commit)流程开始之前调用
Synchronization.beforeCompletion
方法。此调用是使用正在提交的事务的事务上下文执行的。 - 在事务完成后调用
Synchronization.afterCompletion
方法。事务的状态在参数中提供。
3.3.3 事务完成(Transaction Completion)
Transaction.commit
和Transaction.rollback
方法允许目标对象提交(comitted)或回滚事务。调用线程不需要具有与该线程关联的相同事务。如果不允许调用线程提交事务,事务管理器将抛出异常。
如果不允许调用线程提交事务,事务管理器将抛出异常。
3.3.4 事务相等(Equality)和哈希码(Hash Code)
事务管理器必须实现Transaction
对象的equals
方法,以允许在目标对象和另一个Transaction
对象之间进行比较。如果目标对象和参数对象都引用同一个全局事务,则equals
方法应该返回 true
。
例如,当尝试重用已经被一个事务征用(enlisted)的资源时,应用服务器需要比较两个Transaction
对象。这可以使用equals
方法来完成。
Transaction txObj = TransactionManager.getTransaction();
Transaction someOtherTxObj = ..
..
boolean isSame = txObj.equals(someOtherTxObj);
此外,事务管理器必须实现Transaction
对象的 hashCode
方法,以便如果两个Transaction
对象相等,那么它们具有相同的散列代码。但,反过来未必正确。具有相同哈希码的两个事务对象不一定相等。
3.4 XAResource 接口
javax.transaction.xa.XAResource
接口是基于X/Open CAE规范(分布式事务处理:XA规范)的行业标准XA接口的Java映射。
XAResource接口定义了分布式事务处理(DTP)环境中资源管理器和事务管理器之间的契约。资源管理器的资源适配器实现XAResource接口,以支持将全局事务关联到事务资源,例如和关系数据库的连接。
全局事务是在DTP系统中被一个或多个资源管理器(RM)所执行的工作单元。这样的DTP系统依赖于外部事务管理器(如Java transaction Service, JTS)来协调事务。
任何用于由外部事务管理器控制事务环境的应用里的事务资源适配器,都可以支持XAResource接口。比如资源是一个数据库管理系统。一个应用可能通过多个数据库连接访问数据。每一个数据库连接都关联到一个 XAResource 对象,这个XAResource 对象作为底层资源管理器实体的代理对象。事务管理器会为每一个在全局事务里的事务资源创建一个 XAResource。它使用 start
方法去关联资源和事务,使用end
方法去解除资源和事务的关联。资源管理器负责将全局事务与在start
和end
方法调用之间对其数据执行的所有工作关联起来。
在事务提交时,事务管理器通知这些事务资源管理器根据两阶段提交协议准备、提交或回滚事务。
为了更好地与Java环境集成,XAResource接口与标准X/Open XA接口的区别如下:
- 获取资源(连接)时,资源适配器隐式地完成资源管理器初始化。在XAResource接口中没有对应的xa_open。这就避免了资源管理器需要为分布式事务环境和非分布式事务环境提供不同的语法来打开资源。
Rmid
不作为参数传递。我们使用面向对象的方法,其中每个Rmid由一个单独的XAResource对象表示。- 不支持异步操作。Java支持多线程处理,而大多数数据库不支持异步操作。
- 由于事务管理器对XAResource对象处理不当而导致的错误返回值通过XAException类映射到Java异常。
- “控制线程”的DTP概念映射到所有被赋予对XAResource和连接对象访问权的Java线程。例如,两个不同的Java线程在同一个XAResource对象上执行开始和结束操作是合法的(尽管实际上很少使用)。
- 不支持关联迁移和动态注册(optionalX/ openxafe)。为了更加简化XAResource接口和简化资源适配器实现,我们省略了这些特性。
3.4.1 打开资源管理器
X/Open XA接口指定事务管理器必须在任何其他xa_calls之前初始化资源管理器(xa_open)。我们认为,初始化资源管理器的知识应该嵌入标识资源管理器的资源适配器中。事务管理器不需要知道如何初始化资源管理器。TM只负责通知资源管理器何时开始和结束与全局事务关联,以及何时完成事务。
当建立到资源管理器的连接时,资源适配器负责打开(初始化)资源管理器。
3.4.2 关闭资源管理器
如果销毁了事务资源,资源适配器将关闭资源管理器。资源适配器级别的事务资源由两个单独的对象组成:
- 一个XAResource对象,它允许事务管理器启动和结束与正在使用的资源的事务关联,并协调事务完成过程。
- 一个connection对象,它允许应用程序在底层资源上执行操作(例如,RDBMS上的JDBC操作)。
资源管理器一旦打开,就一直保持打开状态,直到显式地释放(关闭)资源。当应用程序调用connection的关闭方法时,资源适配器将使应用程序持有的连接对象引用失效,并将关闭通知应用服务器。事务管理器应该调用XAResource。方法来从该连接中分离事务。
关闭connection的通知让应用服务器执行必要的清理工作,并将物理XA connection标记为空闲,以便重用(如果连接池已就绪)。
3.4.3 线程的控制
X/Open XA接口指定必须从相同的线程上下文中调用与事务关联的XA调用。这种线程控制需求不适用于面向对象的基于组件的应用程序运行时环境,在这种环境中,应用程序线程在方法调用时被动态地分派。如果连接跨越多个方法调用,不同的Java线程可能使用相同的连接资源访问资源管理器。根据应用服务器的实现,同一个XAResource对象可能涉及不同的Java线程。资源上下文和事务上下文可以独立于线程上下文操作。例如,这意味着不同的线程可以调用XAResource.start
和XAResource.end
的方法。
如果应用服务器允许多个线程使用单个XAResource对象和与资源管理器关联的连接,则应用服务器有责任确保在任何时刻只有一个事务上下文与资源关联。
因此,本文档中指定的XAResource接口要求资源管理器能够支持来自任何线程上下文的两阶段提交协议。
3.4.4 事务关联
全局事务通过XAResource.start
与事务资源关联,并通过XAResource.end
与资源断开关联。资源适配器负责在内部维护资源connection对象和XAResource对象之间的关联。在任何给定时间,connection都与单个事务相关联,或者根本不与任何事务相关联。
只要为每个事务上下文切换正确地调用XAResource.start
方法和XAResource.end
方法,事务管理器就可以在使用相同的资源的时候交错多个事务上下文。每次将资源与不同的事务一起使用时,必须为与资源关联的前一个事务调用XAResource.end
方法,并为当前事务上下文调用XAResource.start
方法。
XAResource不支持嵌套事务。如果connection关联的事务于当前事务不同,调用XAResource.start
方法会报错。
3.4.5 外部控制连接
事务应用程序的资源(其事务状态由应用服务器管理)也必须由应用服务器管理,以便正确执行事务关联。如果应用程序与全局事务关联,而连接的资源对象没有与全局事务关联,应用程序通过连接执行事务性工作的时候就会出错。应用服务器必须确保使用的XAResource对象与事务相关联。这是通过调用Transaction.enlistResource
方法来完成的。
如果服务器端事务应用程序跨多个客户端请求保留其数据库连接,则应用服务器必须确保在将客户端请求分派给应用程序线程之前,将资源与应用程序的当前事务上下文一起证用(enlisted)。这意味着应用服务器需要管理跨多个方法调用的连接资源的使用状态。
3.4.6 资源共享
当使用相同的事务资源交错多个事务时,应用服务器有责任确保在任何给定时间只有一个事务被资源征用。要启动事务提交流程,事务管理器可以使用连接到相同资源管理器实例的任何资源对象。用于两阶段提交协议的资源对象不需要参与正在完成(completed)的事务。
资源适配器必须能够处理多个并发调用XAResource方法的线程,以便进行事务提交处理。例如,假设我们有一个事务资源r1。全局事务xid1以r1 started
和ended
。然后,另一个全局事务xid2与r1关联。同时,事务管理器可以使用r1或连接到同一资源管理器的任何其他事务资源启动xid1的两阶段提交过程。当资源与不同的全局事务关联时,资源适配器需要允许执行提交流程。
下面的示例代码演示了上述场景:
// Suppose we have some transactional connection-based
// resource r1 that is connected to an enterprise information
// service system.
//
XAResource xares = r1.getXAResource();
xares.start(xid1); // associate xid1 to the connection
..
xares.end(xid1); // dissociate xid1 to the connection
..
xares.start(xid2); // associate xid2 to the connection
..
// While the connection is associated with xid2,
// the TM starts the commit process for xid1
status = xares.prepare(xid1);
..
xares.commit(xid1, false);
3.4.7 本地和全局事务
鼓励资源适配器支持在同一事务连接(connection)中使用本地事务和全局事务。本地事务是由资源管理器内部启动和协调的事务。XAResource接口不用于本地事务。
当使用相同的连接执行本地和全局事务时,适用以下规则:
- 在连接中启动全局事务之前,必须提交(或回滚)本地事务。
- 在启动任何本地事务之前,必须将全局事务与连接断开关联。
如果资源适配器不支持在同一连接中混合本地事务和全局事务,则资源适配器应抛出特定于资源的异常。例如,如果底层RDBMS的资源管理器不支持在同一个JDBC连接中混合本地事务和全局事务,则会将抛出java.sql.SQLException
给应用程序。
3.4.8 故障恢复
在恢复期间,事务管理器必须能够与系统中应用程序使用的所有资源管理器通信。对于每个资源管理器,事务管理器使用XAResource.recover
方法检索当前处于准备状态或启发式完成状态(heuristically completed)的事务列表。
通常,系统管理员会为部署在系统上的应用程序使用的所有事务资源工厂做配置。这种资源工厂的一个例子是JDBC XADataSource对象,它是JDBC XAConnection对象的工厂。这些事务资源工厂对象的实现既可javax.naming.Referenceable
又可java.io.Serializable
,因此它们可以存储在所有JNDI命名上下文中。
由于XAResource对象不是跨系统故障的持久性对象,因此事务管理器需要某种方法来获取表示资源管理器的XAResource对象,这些资源管理器可能在系统故障之前参与了事务。例如,事务管理器可能通过使用JNDI查找机制和来自应用服务器的协作,获得一个表示系统中配置的每个资源管理器的XAResource对象。然后,事务管理器调用XAResource.recover
方法,要求每个资源管理器返回当前处于准备状态或启发式完成状态的任何事务。事务管理器有责任忽略不属于它的事务。
3.4.9 标识资源管理器实例
事务管理器调用isSameRM方法,以确定目标XAResource对象是否表示与参数中的XAResource对象所表示的相同的资源管理器实例。如果指定的目标对象连接到相同的资源管理器实例,则isSameRM方法返回true;否则,方法返回false。下面的半伪代码说明了预期的用法:
public boolean enlistResource(XAResource xares)
{
..
// Assuming xid1 is the target transaction and
// xid1 already has another resource object xaRes1
// participating in the transaction
boolean sameRM = xares.isSameRM(xaRes1);
if (sameRM) {
//
// Same underlying resource manager instance,
// group together with xaRes1 and join the transaction
//
xares.start(xid1, TMJOIN);
} else { //
// This is a different RM instance,
// make a new transaction branch for xid1
xid1NewBranch = makeNewBranch(xid1);
xares.start(xid1NewBranch, TMNOFLAGS);
}
..
}
3.4.10 动态注册
XAResource不支持动态注册,原因如下:
- 在基于Java组件的应用服务器环境中,当应用程序显式地请求连接时,将动态地获得到资源管理器的连接。这些资源是根据“需要”由事务管理器征募的(与C-XA过程模型中存在的静态xa_switch表不同)。
- 如果资源管理器需要一种将其工作动态注册到全局事务的方法,则可以通过资源适配器和底层资源管理器之间的私有接口在资源适配器级别实现。
3.5 Xid 接口
javax.transaction.xa.Xid
接口是X/Open事务标识符Xid结构的Java映射。此接口指定三个访问器方法,用于检索全局事务的格式ID、全局事务ID和分支限定符。事务管理器和资源管理器使用Xid接口。此接口对应用程序和应用服务器都不可见。
3.6 TransactionSynchronizationRegistry 接口
javax.transaction.TransactionSynchronizationRegistry
接口用于系统级应用服务器组件(如持久性管理器)。这提供了注册 具有特殊排序语义的同步对象、将资源对象与当前事务关联、获取当前事务的事务上下文、获取当前事务状态以及将当前事务标记为回滚的能力。
此接口由应用服务器作为无状态服务对象实现。相同的对象可以线程安全性的被任意数量的组件使用。在标准应用服务器环境中,可以使用标准名称通过JNDI查找实现此接口的实例。
getResource()和putResource()方法的用户是代调用者管理事务数据的库组件。调用方提供的事务数据不会立即刷新到事务征募资源,而是缓存。缓存的数据存储在与事务相关的数据结构中,该结构与调用方的事务上下文保持零或一对一的关系。
管理这种与事务相关的数据结构的一种有效方法是实现TransactionSynchronizationRegistry,为事务状态里的每一个事务管理一个映射(Map)。
这个映射的键是库组件(API的用户)提供的对象。映射的值是库组件感兴趣的存储值,例如与事务相关的数据结构。这个映射没有并发性问题,因为它是事务的专用实例。当事务完成时,将清除映射,释放用于垃圾收集的资源。
将getResource和putResource方法添加到TransactionSynchronizationRegistry中,可以显著增强库代码的可伸缩性。
第5章“Java Transaction API Reference”对此接口有完整的描述。
4 应用服务器中的JTA支持
本章讨论应用服务器支持Java事务API的实现和使用注意事项。这里的讨论假设应用程序的事务和资源使用由应用服务器管理。进一步假设,对底层事务资源管理器的访问是通过资源适配器实现的一些Java API实现的。例如,可以使用JDBC 2.0驱动程序访问关系数据库,可以使用SAP连接器资源适配器访问SAP R/3 ERP系统,等等。本节重点讨论JTA的使用,并假设使用了基于通用连接(connection)的事务资源,而没有指定特定类型的资源管理器。
4.1 基于连接的资源使用场景
假设资源适配器提供了一个基于连接的资源API TransactionalResource来访问底层资源管理器。
在典型的使用场景中,应用服务器调用资源适配器的资源工厂来创建TransactionalResource对象。资源适配器在内部将TransactionalResource与其他两个实体相关联:一个对象实现特定资源适配器的连接(connection)接口,另一个对象实现javax.transaction.xa.XAResource
接口。
应用服务器获取一个TransactionalResource对象,并以以下方式使用它。应用服务器通过getXAResource方法获得XAResource对象。应用服务器使用Transaction.enlistResource
方法将XAResource注册到事务管理器(TM)。TM通过调用XAResource.start
方法,把通知资源管理器执行的工作(通过该连接)与当前与应用程序关联的事务关联起来。
然后,应用服务器调用一些getConnection方法来获取 Connection 对象并将其返回给应用程序。注意,Connection 接口是由资源适配器实现的,它特定于资源管理器支持的底层资源。下图说明了获取资源并将资源注册到事务管理器的一般流程。
在这个使用场景中,XAResource接口对应用程序是透明的,而Connection接口对事务管理器是透明的。应用服务器是惟一持有对某个TransactionalResource对象引用的一方。
下面的代码示例演示了应用服务器如何获取XAResource对象引用并将其注册到事务管理器中。
// Acquire some connection-based transactional resource to
// access the resource manager
Context ctx = InitialContext();
ResourceFactory rf =(ResourceFactory)ctx.lookup(“MyEISResource”);
TransactionalResource res = rf.getTransactionalResource();
// Obtain the XAResource part of the connection and
// enlist it with the Transaction Manager
XAResource xaRes = res.getXAResource();
(TransactionManager.getTransaction()).enlistResource(xaRes);
// get the connection part of the transaction resource
Connection con = (Connection)res.getConnection();
.. return the connection to the application
4.2 事务关联和连接请求流
此会话简要介绍应用服务器如何处理来自应用程序的连接请求。下图说明了JTA的用法。所示的步骤只是为了说明,并不是规定:
- 假设客户端使用TX_REQUIRED事务属性调用EJB bean,并且客户端不与全局事务关联,那么EJB容器通过调用
TransactionManager.Begin
方法启动全局事务。 - 事务启动后,容器调用bean方法。作为业务逻辑的一部分,bean使用相关资源适配器提供的API请求基于连接的资源。
- 应用服务器通过
ResourceFactory.getTransactionalResource
方法从资源适配器获取资源。 - 资源适配器创建TransactionalResource对象和关联的xaresource和connection对象。
- 应用服务器调用getXAResource方法。
- 应用服务器将资源注册到事务管理器。
- 事务管理器调用
XAResource.start
将当前事务关联到资源。 - 应用服务器调用getConnection方法。
- 应用服务器将connection对象引用返回到应用程序。
- 应用程序对连接执行一个或多个操作。
- 应用程序关闭连接。
- 当资源适配器通知connection关闭时,应用服务器将释放(delist)资源。
- 事务管理器调用
XAResource.end
将事务与XAResource分离。 - 应用服务器请求事务管理器提交事务。
- 事务管理器调用
XAResource.prepare
通知资源管理器准备提交事务工作。 - 事务管理器调用
XAResource.commit
来提交事务。
这个例子演示了应用服务器如何使用TransactionManager和XAResource接口作为应用程序连接请求处理的一部分。
5 Java Transaction API Reference
略。
6 Related documents
略。