JTA(Java Transaction API)允许应用程序执行分布式事务处理--在两个或多个网络计算机资源上访问并且更新数据。JDBC驱动程序的JTA支持极大地增强了数据访问能力。
本文的目的是要提供一个关于的Java事务处理API(JTA)的高级的概述,以及与分布式事务相关的内容。一个事务处理定义了一个工作逻辑单元,要么彻底成功要么不产生任何结果。 一个分布式事务处理只是一个在两个或更多网络资源上访问和更新数据的事务处理,因此它在那些资源之间必然是等价的。在本文中,我们主要关心的是如何处理关系数据库系统。
我们要讨论的分布式事务处理(DTP)模型中包含的组件是:
应用程序
应用程序服务器
事务管理程序
资源适配器
资源管理程序
在以后的内容中,我们将描述这些组件以及它们与JTA和数据库访问的关系。
访问数据库
最好把分布式事务处理中包含的组件看作是独立的过程,而不是考虑它们在一个特定的电脑中的位置。这些组件中的一些可以保存在单机中,或者也可在好几台机器之间分布。 下面例子中的图表可以显示在一台特定的电脑上的组件,但是这些操作之间的关系是必须首要考虑的。
最简单的例子:用于本地数据库事务处理的应用程序
关系数据库访问的最简单的形式仅仅包括应用程序、资源管理程序和资源适配器。应用程序只不过是发送请求到数据库并且从数据库中获取数据的最终用户访问点
我们讨论的资源管理程序是一个关系数据库管理系统(RDBMS),比如Oracle或者SQL Server。所有的实际数据库管理都是由这个组件处理的。
资源适配器是外部空间之间的通信管道组件,或者是请求翻译器,在本例中,是应用程序和资源管理程序。在我们的讨论中,这是一个JDBC驱动程序。
下面的描述是资源管理程序本地事务处理的一个描述,也就是说,一个事务处理被被限制在一个特定的企业数据库。
应用程序发送一个用于JDBC驱动程序数据的请求,然后翻译这个请求并把它通过网络发送到数据库中。 数据库把数据发送回驱动程序,然后把翻译的结果发送回应用程序,如下图所示:
这个例子说明了在一个简化的系统中的基本的信息流;然而,今天的企业使用的应用程序服务器都添加了其他的组件到这个过程处理中。
应用程序服务器
应用程序服务器是事务处理操作的另一个组件。应用程序服务器处理大部分的应用程序操作并且获得最终用户应用程序的一些负载。基于前面的例子,我们可以看出应用程序服务器在事务处理上添加了另一个操作层:
import javax.transaction.xa.*; public class MyXid implements Xid { protected int formatId; protected byte gtrid[]; protected byte bqual[]; public MyXid() { } public MyXid(int formatId, byte gtrid[], byte bqual[]) { this.formatId = formatId; this.gtrid = gtrid; this.bqual = bqual; } public int getFormatId() { return formatId; } public byte[] getBranchQualifier() { return bqual; } public byte[] getGlobalTransactionId() { return gtrid; } } |
public DataSource getDataSource() throws SQLException { SQLServerDataSource xaDS = new com.merant.datadirect.jdbcx.sqlserver.SQLServerDataSource(); xaDS.setDataSourceName("SQLServer"); xaDS.setServerName("server"); xaDS.setPortNumber(1433); xaDS.setSelectMethod("cursor"); return xaDS; } |
XADataSource xaDS; XAConnection xaCon; XAResource xaRes; Xid xid; Connection con; Statement stmt; int ret; xaDS = getDataSource(); xaCon = xaDS.getXAConnection("jdbc_user", "jdbc_password"); xaRes = xaCon.getXAResource(); con = xaCon.getConnection(); stmt = con.createStatement(); xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); try { xaRes.start(xid, XAResource.TMNOFLAGS); stmt.executeUpdate("insert into test_table values (100)"); xaRes.end(xid, XAResource.TMSUCCESS); ret = xaRes.prepare(xid); if (ret == XAResource.XA_OK) { xaRes.commit(xid, false); } } catch (XAException e) { e.printStackTrace(); } finally { stmt.close(); con.close(); xaCon.close(); } |
xaRes.start(xid, XAResource.TMNOFLAGS); stmt.executeUpdate("insert into test_table values (100)"); xaRes.end(xid, XAResource.TMSUCCESS); ret = xaRes.prepare(xid); if (ret == XAResource.XA_OK) { xaRes.rollback(xid); } |
xid = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); xaRes.start(xid, XAResource.TMNOFLAGS); stmt.executeUpdate("insert into test_table values (100)"); xaRes.end(xid, XAResource.TMSUSPEND); ∥这个更新在事务范围之外完成,所以它不受XA返回影响。 stmt.executeUpdate("insert into test_table2 values (111)"); xaRes.start(xid, XAResource.TMRESUME); stmt.executeUpdate("insert into test_table values (200)"); xaRes.end(xid, XAResource.TMSUCCESS); ret = xaRes.prepare(xid); if (ret == XAResource.XA_OK) { xaRes.rollback(xid); } |
xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); xid2 = new MyXid(100, new byte[]{0x11}, new byte[]{0x22}); xaRes.start(xid1, XAResource.TMNOFLAGS); stmt.executeUpdate("insert into test_table1 values (100)"); xaRes.end(xid1, XAResource.TMSUCCESS); xaRes.start(xid2, XAResource.TMNOFLAGS); ret = xaRes.prepare(xid1); if (ret == XAResource.XA_OK) { xaRes.commit(xid2, false); } stmt.executeUpdate("insert into test_table2 values (200)"); xaRes.end(xid2, XAResource.TMSUCCESS); ret = xaRes.prepare(xid2); if (ret == XAResource.XA_OK) { xaRes.rollback(xid2); } |
xaDS = getDataSource(); xaCon1 = xaDS.getXAConnection("jdbc_user", "jdbc_password"); xaRes1 = xaCon1.getXAResource(); con1 = xaCon1.getConnection(); stmt1 = con1.createStatement(); xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02}); xaRes1.start(xid1, XAResource.TMNOFLAGS); stmt1.executeUpdate("insert into test_table1 values (100)"); xaRes1.end(xid, XAResource.TMSUCCESS); xaCon2 = xaDS.getXAConnection("jdbc_user", "jdbc_password"); xaRes2 = xaCon1.getXAResource(); con2 = xaCon1.getConnection(); stmt2 = con1.createStatement(); if (xaRes2.isSameRM(xaRes1)) { xaRes2.start(xid1, XAResource.TMJOIN); stmt2.executeUpdate("insert into test_table2 values (100)"); xaRes2.end(xid1, XAResource.TMSUCCESS); } else { xid2 = new MyXid(100, new byte[]{0x01}, new byte[]{0x03}); xaRes2.start(xid2, XAResource.TMNOFLAGS); stmt2.executeUpdate("insert into test_table2 values (100)"); xaRes2.end(xid2, XAResource.TMSUCCESS); ret = xaRes2.prepare(xid2); if (ret == XAResource.XA_OK) { xaRes2.commit(xid2, false); } } ret = xaRes1.prepare(xid1); if (ret == XAResource.XA_OK) { xaRes1.commit(xid1, false); } |
MyXid[] xids; xids = xaRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN); for (int i=0; xids!=null && i try { xaRes.rollback(xids[i]); } catch (XAException ex) { try { xaRes.forget(xids[i]); } catch (XAException ex1) { System.out.println("rollback/forget failed: " + ex1.errorCode); } } } |