JTA (Java transaction API) allows applications to execute Distributed Transaction Processing-access and update data on two or more network computer resources. The JTA support of the JDBC driver greatly enhances data access capabilities.
This article aims to provide an advanced overview of Java transaction processing API (JTA) and content related to distributed transactions. A transaction process defines a logical unit of work, either completely successful or without any results. A distributed transaction is only a transaction that accesses and updates data on two or more network resources. Therefore, it must be equivalent between those resources. In this article, we are mainly concerned about how to process relational database systems.
The components in the Distributed Transaction Processing (DTP) model we will discuss are:
Applications
Application Server
Transaction Management Program
Resource Adapter
Resource Management Program
In the future, we will describe these components and their relationships with JTA and database access.
Access Database
It is better to regard the components contained in Distributed Transaction processing as independent processes, rather than considering their locations in a specific computer. Some of these components can be stored in a single machine or distributed among several machines. The charts in the following example can be displayed on a specific computer, but the relationship between these operations must be considered first.
The simplest example:Applications used for local Database Transaction Processing
The simplest form of relational database access only includes applications, resource management programs, and resource adapters. An application is only an end user access point that sends a request to the database and obtains data from the database.
The Resource Management program we discuss is a relational database management system (RDBMS), such as oracle or SQL Server. All actual database management is handled by this component.
The resource adapter is a Communication Pipeline component between external spaces, or a request translator. In this example, it is an application and a resource manager. In our discussion, this is a JDBC driver.
The following description is a description of the local transaction processing of the Resource Management Program. That is to say, a transaction is restricted to a specific enterprise database.
The application sends a request for JDBC driver data, translates the request, and sends it to the database over the network. The database sends the data back to the driver and then sends a response to the translation result, as shown in:
This example illustrates the basic information flow in a simplified system. However, the application servers used by enterprises today have added other components to this process.
Application Server
The application server is another component for transaction processing. The application server processes most application operations and receives some load from the end user application. Based on the previous example, we can see that the application server has added another operation layer for transaction processing:
So far, our example illustrates the processing of a single local transaction and describes four of the five components of the Distributed Transaction processing model. In the fifth component, the transaction manager starts to be considered only when the transaction is to be allocated.
Distributed Transaction Processing and transaction management program
As we mentioned earlier, a distributed transaction processing is a transaction processing that accesses and updates data on two or more network resources.
These resources can be composed of several different relational database management systems located on a single server, such as Oracle, SQL Server, and Sybase; they can also contain several instances of the same database on several different servers. Under any circumstances, a distributed transaction processing involves the synergy between various resource management programs. This synergy is the transaction management function.
The transaction manager is responsible for making either a commit or a rollback decision on any distributed transaction processing. A commit decision should result in a successful transaction processing, while a return operation will keep the data in the database unchanged. JTA specifies the standard Java interface between the transaction manager in a distributed transaction and another component: application, application server, and resource manager. This relationship is displayed in the following chart:
The numeric box around the Transaction Manager corresponds to the three interfaces of JTA:
1-usertransaction-javax.transaction.usertransaction interface provides applications that can control the transaction processing range programmatically. The javax. transaction. usertransaction method starts a global transaction and associates it with the transaction processing using the calling thread.
2-The transaction Manager-javax.transaction.TransactionManager interface allows the application server to control the transaction scope representing the application being managed.
The 3-xaresource-javax.transaction.xa.xaresource interface is a Java ing Based on the X/Open CAE specification industry standard XA interface.
Note that a restrictive link is supported through the xaresource interface of the JDBC driver. The JDBC driver must support two normal JDBC interactions: applications and/or application servers, and the xaresource section of JTA.
Developers who write application horizontal Code do not care about the details of Distributed Transaction processing management. This is the basic structure of Distributed Transaction Processing-application servers, transaction management programs, and JDBC drivers. The only thing you need to note in the application code is that when the connection is in a distributed transaction range, you should not call a method that will affect the transaction boundary. In particular, an application should not call the Connection Methods commit, rollback, and setautocommit (true) because they will disrupt the basic structure management of distributed transactions.
Distributed Transaction Processing
The transaction Manager program is the basic component of the basic structure of distributed transactions. However, the JDBC driver and application server components should have the following features:
The driver must implement the JDBC 2.0 application interface, including the optional package interface xadatasource, xaconnection, and JTA interface xaresource.
The application server should provide a datasource class for interaction with distributed transactions and a Connection Pool Module (used to improve performance ).
The first step in Distributed Transaction processing is to send a transaction request to the transaction management program. Although the final commit/rollback decides to treat the transaction as a simple logical unit, it may still include many transaction branches. A transaction branch is associated with each resource manager included in a distributed transaction. Therefore, three transaction branches are required for requests managed by three different relational databases. Each transaction branch must be submitted or returned by the local resource manager. The transaction manager controls the transaction boundary and is responsible for final decisions on all transactions that should be committed or returned. This decision consists of two steps, called two-phase commit protocol.
In step 1, the Transaction Manager polls all resource management programs (Relational Database Management) included in distributed transactions to see which one can be submitted. If a resource management program cannot be submitted, it will not respond and return the specific part of the transaction so that the data is not modified.
In step 2, the Transaction Manager determines whether the entire transaction can be returned in the Resource Manager program that denies the response. If no negative response is received, the Translation Manager submits the entire transaction and returns the result to the application.
The developer of the Development event management program code must be related to all three JTA interfaces: usertransaction, transactionmanager, and xaresource.
Sun JTA specification. The JDBC driver developer only needs to care about the xaresource interface. This interface is a Java ing of the industry standard X/Open XA protocol that allows a resource manager to participate in transactions. The driver component connecting to the xaresource interface is responsible for "Translation" tasks between the Transaction Manager program and the Resource Manager program. The following section provides an example of xaresource calling.
JDBC driver and xaresource
To simplify the xaresource description, these examples demonstrate how to use JTA when an application does not contain the application server and the event management program. Basically, the applications in these examples also serve as application servers and event management programs. Most enterprises use transaction management programs and application servers because they can manage distributed transactions more efficiently than an application. However, following these examples, an application developer can test the robustness supported by JTA In the JDBC driver. In addition, some examples may not work on a specific database because of some internal problems associated with the database.
Before using JTA, you must first implement an Xid class to identify the transaction (in general, this will be handled by the transaction manager ). Xid contains three elements: formatid, gtrid (global transaction identifier), and bqual (branch modifier identifier ).
Formatid is usually zero, which means you will use the osi ccr (Open Systems Interconnection commitment, concurrency and recovery standard) to name it. If you use another format, the formatid must be greater than zero. -1 indicates that Xid is invalid.
Gtrid and bqual can contain 64 bytes of binary code to identify global transactions and branch transactions respectively. Gtrid and bqual must be globally unique. In addition, this can be done by using the naming rules specified in OSI Cr.
The following example describes the implementation of Xid:
Import javax. transaction. xa. *; <br/> public class myxid implements Xid <br/> {<br/> protected int formatid; <br/> protected byte gtrid []; <br/> protected byte bqual []; <br/> Public myxid () <br/>{< br/>}< br/> Public myxid (INT formatid, byte gtrid [], byte bqual []) <br/>{< br/> This. formatid = formatid; <br/> This. gtrid = gtrid; <br/> This. bqual = bqual; <br/>}< br/> Public int getformatid () <br/>{< br/> return formatid; <br/>}< br/> Public byte [] getbranchqualifier () <br/>{< br/> return bqual; <br/>}< br/> Public byte [] getglobaltransactionid () <br/>{< br/> return gtrid; <br/>}< br/>
Next, you need to create a data source for the database you want to use:
Public datasource getdatasource () <br/> throws sqlexception <br/> {<br/> sqlserverdatasource xads = new <br/> COM. merant. dataDirect. jdbcx. sqlserver. sqlserverdatasource (); <br/> xads. setdatasourcename ("sqlserver"); <br/> xads. setservername ("server"); <br/> xads. setportnumber (1433); <br/> xads. setselectmethod ("cursor"); <br/> return xads; <br/>}< br/>
Example 1-this example uses the "two-step commit protocol" to submit a transaction Branch:
Xadatasource xads; <br/> xaconnection xacon; <br/> xaresource xares; <br/> Xid; <br/> connection con; <br/> statement stmt; <br/> int ret; <br/> xads = getdatasource (); <br/> xacon = xads. getxaconnection ("jdbc_user", "jdbc_password"); <br/> xares = xacon. getxaresource (); <br/> con = xacon. getconnection (); <br/> stmt = con. createstatement (); <br/> Xid = new myxid (100, new byte [] {0x01}, new byte [] {0x02 }); <br/> try {<br/> xares. start (Xid, xaresource. tmnoflags); <br/> stmt.exe cuteupdate ("insert into test_table values (100)"); <br/> xares. end (Xid, xaresource. tmsuccess); <br/> ret = xares. prepare (Xid); <br/> If (ret = xaresource. xa_ OK) {<br/> xares. commit (Xid, false); <br/>}< br/> catch (xaexception e) {<br/> E. printstacktrace (); <br/>}< br/> finally {<br/> stmt. close (); <br/> con. close (); <br/> xacon. close (); <br/>}
Because the initialization code in all these examples is the same or very similar, but the code is different in some important places.
Example 2-this example is similar to Example 1, indicating a return process:
Xares. start (Xid, xaresource. tmnoflags); <br/> stmt.exe cuteupdate ("insert into test_table values (100)"); <br/> xares. end (Xid, xaresource. tmsuccess); <br/> ret = xares. prepare (Xid); <br/> If (ret = xaresource. xa_ OK) {<br/> xares. rollback (Xid); <br/>}
Example 3-This example shows how a Distributed Transaction branch suspends, enables the same connection to perform local transaction processing, and how to continue the branch later. The two-step commit of distributed transactions does not affect local transactions.
Xid = new myxid (100, new byte [] {0x01}, new byte [] {0x02}); <br/> xares. start (Xid, xaresource. tmnoflags); <br/> stmt.exe cuteupdate ("insert into test_table values (100)"); <br/> xares. end (Xid, xaresource. tmsuspend); <br/> commit this update is completed out of the transaction scope, so it is not affected by XA return. <Br/> stmt.exe cuteupdate ("insert into test_table2 values (111)"); <br/> xares. start (Xid, xaresource. tmresume); <br/> stmt.exe cuteupdate ("insert into test_table values (200)"); <br/> xares. end (Xid, xaresource. tmsuccess); <br/> ret = xares. prepare (Xid); <br/> If (ret = xaresource. xa_ OK) {<br/> xares. rollback (Xid); <br/>}
Example 4-This example shows how an Xa resource shares different transactions. Two transaction branches are created, but they do not belong to the same distributed transaction. JTA allows XA resources to be submitted in two steps on the first branch, although the resource is still associated with the second branch.
Xid1 = new myxid (100, new byte [] {0x01}, new byte [] {0x02}); <br/> xid2 = new myxid (100, new byte [] {0x11}, new byte [] {0x22}); <br/> xares. start (xid1, xaresource. tmnoflags); <br/> stmt.exe cuteupdate ("insert into test_table1 values (100)"); <br/> xares. end (xid1, xaresource. tmsuccess); <br/> xares. start (xid2, xaresource. tmnoflags); <br/> ret = xares. prepare (xid1); <br/> If (ret = xaresource. xa_ OK) {<br/> xares. commit (xid2, false); <br/>}< br/> stmt.exe cuteupdate ("insert into test_table2 values (200)"); <br/> xares. end (xid2, xaresource. tmsuccess); <br/> ret = xares. prepare (xid2); <br/> If (ret = xaresource. xa_ OK) {<br/> xares. rollback (xid2); <br/>}
Example 5-This example shows how transaction branches on different connections connect to a separate branch if they are connected to the same resource manager. This feature improves the efficiency of distributed transactions because it reduces the number of two-step commit processes. Two XA instances connected to the database server will be created. Each connection creates its own XA resources, regular JDBC connections, and statements. Before the second XA resource starts a transaction branch, it checks whether the same resource manager is used as the first XA resource. If this is an instance, it will join the first branch created on the first XA connection, instead of creating a new branch. Later, this transaction branch will use XA resources for preparation and submission.
Xads = getdatasource (); <br/> xacon1 = xads. getxaconnection ("jdbc_user", "jdbc_password"); <br/> xares1 = xacon1.getxaresource (); <br/> con1 = xacon1.getconnection (); <br/> stmt1 = con1.createstatement (); <br/> xid1 = new myxid (100, new byte [] {0x01 }, new byte [] {0x02}); <br/> xares1.start (xid1, xaresource. tmnoflags); <br/> stmt1.executeupdate ("insert into test_table1 values (100)"); <br/> xares1.end (Xid, xaresource. tmsuccess); <br/> xacon2 = xads. getxaconnection ("jdbc_user", "jdbc_password"); <br/> xares2 = xacon1.getxaresource (); <br/> con2 = xacon1.getconnection (); <br/> stmt2 = con1.createstatement (); <br/> If (xares2.issamerm (xares1) {<br/> xares2.start (xid1, xaresource. tmjoin); <br/> stmt2.executeupdate ("insert into test_table2 values (100)"); <br/> xares2.end (xid1, xaresource. tmsuccess); <br/>}< br/> else {<br/> xid2 = new myxid (100, new byte [] {0x01 }, new byte [] {0x03}); <br/> xares2.start (xid2, xaresource. tmnoflags); <br/> stmt2.executeupdate ("insert into test_table2 values (100)"); <br/> xares2.end (xid2, xaresource. tmsuccess); <br/> ret = xares2.prepare (xid2); <br/> If (ret = xaresource. xa_ OK) {<br/> xares2.commit (xid2, false); <br/>}< br/> ret = xares1.prepare (xid1 ); <br/> If (ret = xaresource. xa_ OK) {<br/> xares1.commit (xid1, false); <br/>}
Example 6-This example shows how to restore the prepared or almost completed transaction branch in the error recovery phase. It first tries to return each branch; if it fails, it tries to let the resource manager lose the message about the transaction.
Myxid [] xids; <br/> xids = xares. Recover (xaresource. tmstartrscan | xaresource. tmendrscan); <br/> for (INT I = 0; xids! = NULL & I <br/> try {<br/> xares. rollback (xids [I]); <br/>}< br/> catch (xaexception ex) {<br/> try {<br/> xares. forget (xids [I]); <br/>}< br/> catch (xaexception ex1) {<br/> system. out. println ("rollback/Forget failed:" + ex1.errorcode); <br/>}< br/>}