什么是 JTS?
JTS 是一个 组件事务监视器(component transaction monitor)。这是什么意思?我们将介绍事务处理监视器(TPM)这个概念,TPM 是一个程序,它代表应用程序协调分布式事务的执行。TPM 与数据库出现的时间长短差不多;在 60 年代后期,IBM 首先开发了 CICS,至今人们仍在使用。经典的(或者说 程序化)TPM 管理被程序化定义为针对事务性资源(比如数据库)的操作序列的事务。随着分布式对象协议,如 CORBA、DCOM 和 RMI 的出现,人们希望看到事务更面向对象的前景。将事务性语义告知面向对象的组件要求对 TPM 模型进行扩展 ― 在这个模型中事务是按照事务性对象的调用方法定义的。JTS 只是一个组件事务监视器(有时也称为 对象事务监视器(object transaction monitor)),或称为 CTM。
JTS 和 J2EE 的事务支持设计受 CORBA 对象事务服务(CORBA Object Transaction Service,OTS)的影响很大。实际上,JTS 实现 OTS 并充当 Java 事务 API(Java Transaction API)― 一种用来定义事务边界的低级 API ― 和 OTS 之间的接口。使用 OTS 代替创建一个新对象事务协议遵循了现有标准,并使 J2EE 和 CORBA 能够互相兼容。
乍一看,从程序化事务监视器到 CTM 的转变好像只是术语名称改变了一下。然而,差别不止这一点。当 CTM 中的事务提交或回滚时,与事务相关的对象所做的全部更改都一起被提交或取消。但 CTM 怎么知道对象在事务期间做了什么事?象 EJB 组件之类的事务性组件并没有 commit() 或 rollback() 方法,它们也没向事务监视器注册自己做了什么事。那么 J2EE 组件执行的操作如何变成事务的一部分呢?
透明的资源征用
当应用程序状态被组件操纵时,它仍然存储在事务性资源管理器(例如,数据库和消息队列服务器)中,这些事务性资源管理器可以注册为分布式事务中的资源管理器。在第 1 部分中,我们讨论了如何在单个事务中征用多个资源管理器,事务管理器如何协调这些资源管理器。资源管理器知道如何把应用程序状态中的变化与特定的事务关联起来。
但这只是把问题的焦点从组件转移到了资源管理器 ― 容器如何断定什么资源与该事务有关,可以供它征用?请考虑下面的代码,在典型的 EJB 会话 bean 中您可能会发现这样的代码:
清单 1. bean 管理的事务的透明资源征用
1
InitialContext ic
=
new
InitialContext();
2
UserTransaction ut
=
ejbContext.getUserTransaction();
3
ut.begin();
4
DataSource db1
=
(DataSource) ic.lookup(
"
java:comp/env/OrdersDB
"
);
5
DataSource db2
=
(DataSource) ic.lookup(
"
java:comp/env/InventoryDB
"
);
6
Connection con1
=
db1.getConnection();
7
Connection con2
=
db2.getConnection();
8
//
perform updates to OrdersDB using connection con1
9
//
perform updates to InventoryDB using connection con2
10
ut.commit();
11
注意,这个示例中没有征用当前事务中 JDBC 连接的代码 ― 容器会为我们完成这个任务。我们来看一下它是如何发生的。
资源管理器的三种类型
当一个 EJB 组件想访问数据库、消息队列服务器或者其它一些事务性资源时,它需要到资源管理器的连接(通常是使用 JNDI)。而且,J2EE 规范只认可三种类型的事务性资源 ― JDBC 数据库、JMS 消息队列服务器和“其它通过 JCA 访问的事务性服务”。后面一种服务(比如 ERP 系统)必须通过 JCA(J2EE Connector Architecture,J2EE 连接器体系结构)访问。对于这些类型资源中的每一种,容器或提供者都会帮我们把资源征调到事务中。
在清单 1 中, con1 和 con2 好象是普通的 JDBC 连接,比如那些从 DriverManager.getConnection() 返回的连接。我们从一个 JDBC DataSource 得到这些连接,JDBC DataSource 可以通过查找 JNDI 中的数据源名称得到。EJB 组件中被用来查找数据源( java:comp/env/OrdersDB )的名称是特定于组件的;组件的部署描述符的 resource-ref 部分将其映射为容器管理的一些应用程序级 DataSource 的 JNDI 名称。
隐藏的 JDBC 驱动 器
每个 J2EE 容器都可以创建有事务意识的池态 DataSource 对象,但 J2EE 规范并不向您展示如何创建,因为这不在 J2EE 规范内。浏览 J2EE 文档时,您找不到任何关于如何创建 JDBC 数据源的内容。相反,您不得不为您的容器查阅该文档。创建一个数据源可能需要向属性或配置文件添加一个数据源定义,或者也可以通过 GUI 管理工具完成,这取决于您的容器。
每个容器(或连接池管理器,如 PoolMan)都提供它自己的创建 DataSource 机制,JTA 魔术就隐藏在这个机制中。连接池管理器从指定的 JDBC 驱动器得到一个 Connection ,但在将它返回到应用程序之前,将它与一个也实现 Connection 的虚包包在一起,将自己置入应用程序和底层连接之间。当创建连接或者执行 JDBC 操作时,包装器询问事务管理器当前线程是不是正在事务的上下文中执行,如果事务中有 Connection 的话,就自动征用它。
其它类型的事务性资源,JMS 消息队列和 JCA 连接器,依靠相似的机制将资源征用隐藏起来,使用户看不到。如果要使 JMS 队列在部署时对 J2EE 应用程序可用,您就要再次使用特定于提供者的机制来创建受管 JMS 对象(队列连接工厂和目标),然后在 JNDI 名称空间内发布这些对象。提供者创建的受管对象包含与 JDBC 包装器(由容器提供的连接池管理器添加)相似的自动征用代码。