副标题#e#
引言
JTA(Java Transaction API)答允应用措施执行漫衍式事务处理惩罚–在两个或多个网络计较机资源上会见而且更新数据。JDBC驱动措施的JTA支持极大地加强了数据会见本领。
本文的目标是要提供一个关于的Java事务处理惩罚API(JTA)的高级的概述,以及与漫衍式事务相关的内容。一个事务处理惩罚界说了一个事情逻辑单位,要么彻底乐成要么不发生任何功效。一个漫衍式事务处理惩罚只是一个在两个或更多网络资源上会见和更新数据的事务处理惩罚,因此它在那些资源之间一定是等价的。在本文中,我们主要体贴的是如那里理惩罚干系数据库系统。
我们要接头的漫衍式事务处理惩罚(DTP)模子中包括的组件是:
应用措施
应用措施处事器
事务打点措施
资源适配器
资源打点措施
在今后的内容中,我们将描写这些组件以及它们与JTA和数据库会见的干系。
会见数据库
最好把漫衍式事务处理惩罚中包括的组件看作是独立的进程,而不是思量它们在一个特定的电脑中的位置。这些组件中的一些可以生存在单机中,可能也可在好几台呆板之间漫衍。下面例子中的图表可以显示在一台特定的电脑上的组件,可是这些操纵之间的干系是必需首要思量的。
最简朴的例子:用于当地数据库事务处理惩罚的应用措施
干系数据库会见的最简朴的形式仅仅包罗应用措施、资源打点措施和资源适配器。应用措施只不外是发送请求到数据库而且从数据库中获取数据的最终用户会见点
我们接头的资源打点措施是一个干系数据库打点系统(RDBMS),好比Oracle可能SQL Server。所有的实际数据库打点都是由这个组件处理惩罚的。
资源适配器是外部空间之间的通信管道组件,可能是请求翻译器,在本例中,是应用措施和资源打点措施。在我们的接头中,这是一个JDBC驱动措施。
下面的描写是资源打点措施当地事务处理惩罚的一个描写,也就是说,一个事务处理惩罚被被限制在一个特定的企业数据库。
应用措施发送一个用于JDBC驱动措施数据的请求,然后翻译这个请求并把它通过网络发送到数据库中。数据库把数据发送回驱动措施,然后把翻译的功效发送回应用措施,如下图所示:
这个例子说明白在一个简化的系统中的根基的信息流;然而,本日的企业利用的应用措施处事器都添加了其他的组件到这个进程处理惩罚中。
#p#副标题#e#
应用措施处事器
应用措施处事器是事务处理惩罚操纵的另一个组件。应用措施处事器处理惩罚大部门的应用措施操纵而且得到最终用户应用措施的一些负载。基于前面的例子,我们可以看出应用措施处事器在事务处理惩罚上添加了另一个操纵层:
到今朝为止,我们的例子说明白单个的当地事务处理惩罚,而且描写了漫衍式事务处理惩罚模子的五个组件中的四个。第五个组件,事务打点措施只有当事务将要被分派的时候才会开始被思量。
漫衍式事务处理惩罚和事务打点措施
像我们前面所提到的,一个漫衍式事务处理惩罚是一个在两个或更多网络资源上会见和更新数据的事务处理惩罚。
这些资源可以由好几个位于一个单独处事器上的差异的干系型数据库打点系统构成,好比说Oracle、SQL Server和Sybase;它们也可以包括存在于若干差异的处事器上的同一种数据库的若干个实例。在任何环境下,一个漫衍式事务处理惩罚包罗各类的资源打点措施之间的协同浸染。这个协同浸染是事务打点函数。
事务打点措施认真作出要么提交(commit)要么退回(rollback)任何漫衍式事务处理惩罚的抉择。一个提交抉择应该导致一个乐成的事务处理惩罚;而退回操纵则是保持数据库中的数据稳定。JTA指定一个漫衍式事务处理惩罚中的事务打点措施和另一个组件之间的尺度Java接口:应用措施,应用措施处事器和资源打点措施。这个干系被显示在下面的图表中:
在事务打点措施周围的数字框框相应于JTA的三个接口部门:
1—UserTransaction—javax.transaction.UserTransaction接口提供可以或许编程地节制事务处理惩罚范畴的应用措施。javax.transaction.UserTransaction要领开启一个全局事务而且利用挪用线程与事务处理惩罚关联。
2—Transaction Manager—javax.transaction.TransactionManager接口答允应用措施处事器来节制代表正在打点的应用措施的事务范畴。
3—XAResource—javax.transaction.xa.XAResource接口是一个基于X/Open CAE Specification的行业尺度XA接口的Java映射。
#p#分页标题#e#
留意,一个限制性环节是通过JDBC驱动措施的XAResource接口的支持。JDBC驱动措施必需支持两个正常的JDBC交互浸染:应用措施和/或应用措施处事器,并且以及JTA的XAResource部门。
编写应用措施程度代码的开拓者不会体贴漫衍式事务处理惩罚打点的细节。这是漫衍式事务处理惩罚根基布局的事情—应用措施处事器、事务打点措施和JDBC驱动措施。应用措施代码中独一的需要留意的就是当毗连处于一个漫衍式事务范畴内的时候,不该该挪用一个会影响事务界线的要领。出格的是,一个应用措施不该该挪用Connection要领commit、rollback和setAutoCommit(true),因为它们将粉碎漫衍式事务的根基布局打点。
漫衍式事务处理惩罚
事务打点措施是漫衍式事务根基布局的根基组件;然而JDBC驱动措施和应用措施处事器组件应该具备下面的特征:
驱动措施应该实现JDBC 2.0应用措施接口,包罗Optional Package接口XADataSource和XAConnection以及JTA接口XAResource。
应用措施处事器应该提供一个DataSource类,用来实现与漫衍式事务根基结的交互以及一个毗连池模块(用于改进机能)。
漫衍式事务处理惩罚的第一步就是应用措施要发送一个事务请求到事务打点措施。固然最后的commit/rollback抉择把事务作为一个简朴的逻辑单位来看待,可是仍然大概会包罗很多事务分支。一个事务分支与一个到包括在漫衍式事务中的每个资源打点措施相关联。因此,到三个差异的干系数据库打点的请求需要三个事务分支。每个事务分支必需由当地资源打点措施提交可能返回。事务打点措施节制事务的界线,而且认真最后抉择应该提交可能返回的全部事务。这个抉择由两个步调构成,称为Two – Phase Commit Protocol。
在第一步调中,事务打点措施轮询所有包括在漫衍式事务中的资源打点措施(干系数据库打点)来看看哪个可以筹备提交。假如一个资源打点措施不能提交,它将不响应,而且把事务的特定部门返回,以便数据不被修改。
在第二步调中,事务打点措施判定否认响应的资源打点措施中是否有可以或许返回整个事务的。假如没有否认响应的话,翻译打点措施提交整个事务而且返回功效到应用措施中。
开拓事项打点措施代码的开拓者必需与所有三个JTA接口有关:UserTransaction、TransactionManager和XAResource,这三个接口都被描写在
Sun JTA specification中。JDBC驱动措施开拓者只需要体贴XAResource接口。这个接口是答允一个资源打点措施参加事务的行业尺度X/Open XA协议的Java映射。毗连XAResource接口的驱动措施组件认真在事务打点措施和资源打点措施之间接受"翻译"的任务。下面的章节提供了XAResource挪用的例子。
JDBC驱动措施和XAResource
为了简化XAResource的说明,这些例子说明白一个应用措施在不包括应用措施处事器和事项打点措施的环境下应该如何利用JTA。根基上,这些例子中的应用措施也接受应用措施处事器和事项打点措施的任务。大部门的企业利用事务打点措施和应用措施处事器,因为它们可以或许比一个应用措施更可以或许高效地打点漫衍式事务。然而遵循这些例子,一个应用措施开拓者可以测试在JDBC驱动措施中的JTA支持的结实性。并且有一些例子大概不是事情在某个特定的数据库上,这是因为关联在数据库上的一些内涵的问题。
在利用JTA之前,你必需首先实现一个Xid类用来标识事务(在普通环境下这将由事务打点措施来处理惩罚)。Xid包括三个元素:formatID、gtrid(全局事务标识符)和bqual(分支修饰词标识符)。
formatID凡是是零,这意味着你将利用OSI CCR(Open Systems Interconnection Commitment, Concurrency和Recovery 尺度)来定名。假如你要是用别的一种名目,那么formatID应该大于零。-1值意味着Xid为无效。
gtrid和bqual可以包括64个字节二进制码来别离标识全局事务和分支事务。独一的要求是gtrid和bqual必需是全局独一的。另外,这可以通过利用指定在OSI CCR中的定名法则类型来完成。
下面的例子说明Xid的实现:
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;
}
}
其次,你需要建设一个你要利用的数据库的数据源:
#p#分页标题#e#
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;
}
例1—这个例子是用“两步提交协议”来提交一个事务分支:
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();
}
因为所有这些例子中的初始化代码沟通可能很是相似,仅仅是一些重要的处所的代码由差异。
例2—这个例子,与例1相似,说明白一个返回进程:
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);
}
例3—这个例子说明一个漫衍式事务分支如何中止,让沟通的毗连做当地事务处理惩罚,以及它们稍后该如何继承这个分支。漫衍式事务的两步提交浸染不影响当地事务。
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);
}
例4—这个例子说明一个XA资源如何分管差异的事务。建设了两个事务分支,可是它们不属于沟通的漫衍式事务。JTA答允XA资源在第一个分支上做一个两步提交,固然这个资源仍然与第二个分支相关联。
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);
}
例5—这个例子说明差异的毗连上的事务分支如何毗连成为一个单独的分支,假如它们毗连到沟通的资源打点措施。这个特点改进了漫衍式事务的效率,因为它淘汰了两步提交处理惩罚的数目。两个毗连到数据库处事器上的XA将被建设。每个毗连建设它本身的XA资源,正规的JDBC毗连和语句。在第二个XA资源开始一个事务分支之前,它将察看是否利用和第一个XA资源利用的是同一个资源打点措施。假如这是实例,它将插手在第一个XA毗连上建设的第一个分支,而不是建设一个新的分支。稍后,这个事务分支利用XA资源来筹备和提交。
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);
}
#p#分页标题#e#
例6—这个例子说明在错误规复的阶段,如何规复筹备好的可能将近完成的事务分支。它首先试图返回每个分支;假如它失败了,它实验着让资源打点措施丢掉关于事务的动静。
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);
}
}
}