JTA Deep Adventures-Principles and implementation

Source: Internet
Author: User

In the Java EE application, transactions are an indispensable component model that guarantees the ACID (i.e. atomic, consistent, isolated, persistent) properties of user operations. For applications that operate only a single data source, transaction management can be implemented through a local resource interface, and for large applications that span data sources, such as multiple databases or databases and JMS, you must use the global transaction JTA (Java Transaction API). JTA provides distributed transaction services for the Java EE platform, which isolates transactions from underlying resources and enables transparent transaction management. This article will delve into the architecture of JTA and introduce its implementation mechanism through detailed code.

Processing Transactions using JTA

What is transaction processing

Transaction is an indispensable component model in computer application, which guarantees the atomicity (atomicity), consistency (consistency), isolation (isolation) and persistence (durabilily) of user operation. The most classic example of a transaction is a credit card transfer: Transfer 500 RMB to user B's account in user A's account with the following procedure
1. Reduce the amount in account a by 500
2. Increase the amount in the B account by 500
Both operations must have a positive ACID transaction attribute: either all succeeds or all fails, and if there is no transaction protection, the user's account amount may be problematic:
If the first step succeeds and the second fails, then the amount in user A's account will be reduced by $500 and User B's account will have no increase (missing); Also if the first step is wrong and the second succeeds, then user A's account amount is unchanged and User B's account is increased by 500 yuan (born out of thin air). Any of these errors can result in serious data inconsistency, and the absence of a transaction is unacceptable for a stable production system.

How to handle the Java EE transaction

1. Local transactions: Tightly dependent on the underlying resource manager (such as a database connection), transaction processing is limited to the current transaction resource. This type of transaction does not have a dependency on the application server, so the deployment is flexible but does not support distributed transactions with multiple data sources. Examples of using local transactions in a database connection are as follows:

Listing 1. Local transaction instance
public void Transferaccount () {  Connection conn = null;  Statement stmt = null;  try{  conn = Getdatasource (). getconnection ();  Conn.setautocommit (false); stmt = Conn.createstatement ();  Reduce the amount in account a by up to  Stmt.execute ("             update t_account Set amount = amount-500 where account_id = ' a '");//Place B in the account The amount increased by  Stmt.execute ("             update t_account Set amount = Amount + where account_id = ' B '");//COMMIT TRANSACTION     C Onn.commit (); Transaction commit: The two-step operation of the transfer succeeds at the same time} catch (SQLException Sqle) {  try{  //exception occurred, rollback in this transaction                Conn.rollback (); /transaction rollback: The two-step operation of the transfer is completely revoked                 stmt.close ();                  Conn.close ();  } catch (Exception ignore) {  }  sqle.printstacktrace ();  }  }

2. Distributed transactions: the Java Transaction programming interface (Jta:java Transaction API) and the Java Transaction Services (Jts;java Transaction Service) provide distributed transaction services for the EE platform. Distributed transactions (distributed Transaction) include the transaction manager (Transaction Manager) and one or more resource managers (Resource Manager) that support the XA protocol. We can look at the resource manager as any type of persistent data storage, and the transaction manager undertakes the coordination and control of all the participating units of the transaction. The JTA transaction effectively masks the underlying transaction resources, enabling the application to be transparently involved in the transaction, but the system overhead of the XA protocol is larger than the local transaction, and it is prudent to consider whether a distributed transaction is really needed during system development. If you do need a distributed transaction to coordinate multiple transactional resources, you should implement and configure the transactional resources that support the XA protocol, such as JMS, JDBC database connection pooling, and so on. Examples of processing transactions using JTA are as follows (note: Conna and CONNB are connections from different databases)

Listing 2. JTA Transaction Processing
public void Transferaccount () {usertransaction usertx = null;  Connection Conna = null;  Statement Stmta = null;  Connection CONNB = null;      Statement STMTB = null;  try{//Get Transaction Management Object USERTX = (usertransaction) getcontext (). Lookup ("Java:comp/usertransaction");  Get database connection from database A Conna = Getdatasourcea (). getconnection ();                               Get the database connection from database B CONNB = Getdatasourceb (). getconnection (); Start a transactionUsertx.begin ();Reduce the amount in a account by up to Stmta = Conna.createstatement (); Stmta.execute ("update t_account set amount = amount-500 where account_id = ' A '");  Increase the amount in the B account by $ stmtb = Connb.createstatement (); Stmtb.execute ("update t_account Set amount = Amount + where account_id = ' B '"); Commit a transactionusertx.commit ();Transaction commit: The two-step operation of the transfer succeeds at the same time (data in database A and database B is updated simultaneously)} catch (SQLException Sqle) {try{//exception, rollback of manipulation in this transactionUsertx.rollback ();Transaction rollback: Two-step operation of the transfer is completely revoked//(Data updates in database A and database B are revoked simultaneously) stmt.close ();  Conn.close (); ...  }  catch (Exception ignore) {} sqle.printstacktrace ();  } catch (Exception ne) {e.printstacktrace (); }  }
JTA Implementation principle

Many developers are interested in JTA's internal work mechanism: The code I write does not have any code to interact with transactional resources (such as database connections), but my operations (database updates) are actually included in the transaction, and how does JTA achieve this transparency? To understand the JTA implementation principle, you need to understand its schema first: It includes the transaction manager (Transaction Manager) and one or more XA protocol-supporting resource managers (Resource Manager), we can view the resource Manager as any type of persisted data Storage; The transaction manager is responsible for the coordination and control of all the transaction participation units. Depending on the object-oriented, we can understand the JTA transaction manager and the resource manager as two aspects: the usage interface (transaction manager) for the developer and the implementation interface (Resource Manager) for the service provider. The main part of the development interface is the UserTransaction object referenced in the example above, in which the developer implements the distributed transaction in the information system, and the implementation interface is used to standardize the transaction service provided by the provider (such as the database connection provider), which contracts the resource management function of the transaction. Enables JTA to perform collaborative communication between heterogeneous transaction resources. In the case of databases, IBM provides a database driver for distributed transactions, and Oracle provides a database driver for distributed transactions, while simultaneously using DB2 and Oracle two database connections, JTA That is, you can implement distributed transactions based on the two transactional resources of the agreed interface coordinator. It is the different implementations that are based on uniform specifications that enable JTA to coordinate and control transactional resources for different databases or JMS vendors, as shown in the architecture:

Figure 1. JTA Architecture

Developers use the developer interface to enable application support for global transactions, and each provider (database, JMS, etc.) provides transactional resource management capabilities based on the specifications of the provider interface; transaction manager (TransactionManager Maps the use of distributed transactions to the actual transactional resources and coordinates and controls the transaction resources. Here, the three main interfaces, including UserTransaction, Transaction, and TransactionManager, as well as their defined methods, are described in this article.

The developer-oriented interface is usertransaction (as shown in the previous example), and developers typically use this interface only to implement JTA transaction management, which defines the following methods:

    • Begin ()-Begins a distributed transaction (in the background TransactionManager creates a Transaction transaction object and associates the object with Threadlocale on the current thread)
    • commit ()-Commit transaction (in background TransactionManager will fetch the transaction object from the current thread and commit the transaction represented by this object)
    • rollback ()-rollback TRANSACTION (in background TransactionManager The transaction object is fetched from the current thread and the transaction represented by this object is rolled back)
    • getStatus ()-Returns the state of a distributed transaction that is associated to the current thread (the status object defines all transaction states, and interested readers can refer to the API documentation)
    • setrollbackonly ()-Identifies the distributed transaction that is associated to the current thread will be rolled back

Provider-oriented implementation interfaces mainly involve TransactionManager and Transaction two objects

Transaction represents a physical transaction, and TransactionManager creates a Transaction when the developer calls the Usertransaction.begin () method. The transaction object (which marks the beginning of the transaction) and associates this object to the current thread through Threadlocale. Methods such as commit (), rollback (), getStatus () in the UserTransaction interface are ultimately delegated to the corresponding method of the Transaction class. The Transaction interface defines the following methods:

    • commit () -Reconcile different transaction resources to complete the commit of the transaction
    • rollback () -Reconcile different transactional resources to complete the rollback of a transaction
    • setrollbackonly () -identifies a distributed transaction that is associated to the current thread will be rolled back
    • GetStatus () -Returns the status of the distributed transaction associated to the current thread
    • enlistresource (XAResource xares, int flag) - Joins the transaction resource into the current transaction (in the above example, the transaction resource that it represents when it operates on database A is associated with the current transaction, as well as the transaction resource it represents when it operates on database B)
    • DELISTRESOURC (XAResource xares, int flag) -Removes the transaction resource from the current transaction
    • registersynchronization ( Synchronization sync) -callback interface, Hibernate and other ORM tools have their own transaction control mechanism to ensure transactions, but at the same time they also need a callback mechanism to be notified when the transaction is complete to trigger some processing work, such as clearing the cache. This involves the callback interface registersynchronization of the Transaction. The tool can inject the callback program into the transaction through this interface, and when the transaction is successfully committed, the callback program is activated.

TransactionManager itself does not assume the actual transaction processing function, it is more to act as a bridge between the user interface and the implementation interface. The methods defined in TransactionManager are listed below, and you can see that most of the transaction methods in this interface are identical to UserTransaction and Transaction. When the developer calls the Usertransaction.begin () method, TransactionManager creates a Transaction transaction object (which marks the beginning of the transaction) and associates the object with Threadlocale on the current thread The same usertransaction.commit () will call Transactionmanager.commit (), which will fetch the transaction object Transaction from the current thread and commit the transaction represented by the object, that is, invoking the Transaction.commit ()

    • Begin ()-Start transaction
    • commit ()-Commit Transaction
    • rollback ()-ROLLBACK TRANSACTION
    • getStatus ()-Returns the current transaction state
    • Setrollbackonly ()
    • gettransaction ()-Returns the transaction associated to the current thread
    • settransactiontimeout (int seconds)-Set transaction time-out time
    • Resume (Transaction tobj)-Continue the transaction associated with the thread
    • suspend ()-suspends the transaction associated with the current thread

During system development, you encounter an operation that needs to be temporarily excluded from the transaction resource, and you need to call the Suspend () method to suspend the current transaction: any operations that are made after this method will not be included in the transaction, and resume () is called after the non-transactional operation completes (note: To do this, you need to obtain the TransactionManager object, which is not available on different Java EE application servers.
The following will introduce the JTA implementation principle to the reader through specific code. Lists the Java classes involved in the example implementation, where Usertransactionimpl implements the UserTransaction interface, Transactionmanagerimpl implements the TransactionManager interface, Transactionimpl implements the Transaction interface

Figure 2. JTA Implementation class Diagram listing 3. Start transaction-Usertransactionimpl implenments usertransaction
public void Begin () throws NotSupportedException, SystemException {    //delegate operation of start transaction to Transactionmanagerimpl    Transactionmanagerimpl.singleton (). Begin ();      
Listing 4. Start transaction-Transactionmanagerimpl implements TransactionManager
Here Transactionholder is used to associate the transaction object represented by Transaction on the thread private static threadlocal<transactionimpl> Transactionholder         = new threadlocal<transactionimpl> ();  Transacationmananger must maintain a global object, so use single-instance mode to implement private static Transactionmanagerimpl singleton = new Transactionmanagerimpl ();  Private Transactionmanagerimpl () {  } public  static Transactionmanagerimpl Singleton () {  return singleton;  } Public  Void Begin () throws NotSupportedException, SystemException {  //xidimpl implements the Xid interface, which uniquely identifies a transaction Xidimpl XI D = new Xidimpl ();  Creates a transaction object and associates the object to the thread Transactionimpl tx = new Transactionimpl (XID);  Transactionholder.set (TX);  }

Now we can understand why the Begin method is not defined on the Transaction interface: The Transaction object itself represents a transaction, and when it is created, it indicates that the transaction has started, so there is no need to define the begin () method.

Listing 5. Commit Transaction-Usertransactionimpl implenments usertransaction
      public void Commit () throws Rollbackexception, Heuristicmixedexception,  heuristicrollbackexception, SecurityException,  illegalstateexception, systemexception {//check to see if the rollback is the only  transaction, if the transaction is rolled back if        ( rollbackonly) {      rollback ();      return;        } else {     //delegate The operation committing the transaction to Transactionmanagerimpl     Transactionmanagerimpl.singleton (). commit ();        }  }
Listing 6. Commit Transaction-Transactionmanagerimpl implenments TransactionManager
public void Commit () throws Rollbackexception, Heuristicmixedexception,     heuristicrollbackexception, SecurityException,     illegalstateexception, SystemException {      //Gets the transactions associated with the current transaction and commits     it through its commit method Transactionimpl tx = Transactionholder.get ();      Tx.commit ();          }

Similarly, methods such as rollback, getStatus, and Setrollbackonly are implemented in the same way as commit (). The UserTransaction object does not have any control over the transaction, and all transaction methods are passed through TransactionManager to the actual transaction resource, the Transaction object.
The above example demonstrates the processing of the JTA transaction, which shows you how the transactional resource (database connection, JMS) is joined transparently to the JTA transaction. The first thing to make clear is that the database source (DataSource) obtained in the JTA transaction code must support distributed transactions. In the following code example, although all database operations are included in the JTA transaction, any updates to MySQL will not be automatically included in the global transaction because the MySQL database connection is obtained locally.

Listing 7. JTA Transaction Processing
public void Transferaccount () {usertransaction usertx = null;  Connection mysqlconnection = null;  Statement mysqlstat = null;  Connection CONNB = null;      Statement STMTB = null; try{//Get Transaction Management Object USERTX = (usertransaction) getcontext (). Lookup ("Java:comp/usertransaction") ;  Local access to MYSQL database connection mysqlconnection = Drivermanager.getconnection ("localhost:1111");                               From database B to get the database connection, GETDATASOURCEB returns the application server's data source CONNB = Getdatasourceb (). getconnection (); Start a transactionUsertx.begin ();Reduce the amount in account A to//mysqlconnection is a locally obtained database connection and will not be included in the global transaction Mysqlstat = Mysqlconnection.createstatement (); Mysqlstat.execute ("update t_account set amount = amount-500 where account_id = ' A '");  CONNB is a database connection from the application server, which is included in the global transaction STMTB = Connb.createstatement (); Stmtb.execute ("update t_account Set amount = Amount + where account_id = ' B '"); Transaction commit: CONNB operation is committed, mysqlconnection operation is not committedusertx.commit ();} catch (SQLException Sqle) {//Handle exception code} catch (Exception ne) {e.printstacktrace (); }  }

Why is a database connection that must be obtained from a data source that supports transactions to support distributed transactions? In fact, the data source supporting the transaction is different from the normal data source, it implements the additional Xadatasource interface. We can simply interpret xadatasource as a normal data source (inherited from Java.sql.PooledConnection), but it adds Getxaresource methods to support distributed transactions. In addition, the database connection returned by Xadatasource is also different from the normal connection, which implements the Xaconnection interface in addition to all the functions defined by the java.sql.Connection. We can understand xaconnection as a normal database connection, which supports database operations for all JDBC specifications, except that xaconnection adds support for distributed transactions. The following class diagram readers can understand the relationship of these interfaces:

Figure 3. Transaction Resource class Diagram

The database connection that an application obtains from a data source that supports distributed transactions is an implementation of the Xaconnection interface, and the session created by this database connection (Statement) also adds functionality to support distributed transactions, as shown in the following code:

Listing 8. JTA Transaction Resource Processing
public void Transferaccount () {  usertransaction usertx = null;  Connection conn = null;  Statement stmt = null;      try{         //Get Transaction Management Object USERTX = (usertransaction) getcontext (). Lookup ("Java:comp/usertransaction");  To get the database connection from the database, GETDATASOURCEB returns the data source that supports the distributed transaction conn = Getdatasourceb (). getconnection ();                         Session stmt has been enhanced to support distributed transactions stmt = Conn.createstatement ();                         Usertx.begin ();             Stmt.execute ("Update t_account ... where account_id = ' A '");  usertx.commit (); } catch (SQLException Sqle) {  //Handle exception code} catch (Exception ne) {  e.printstacktrace ();  }  }

Let's take a look at the code implementation of the session (Statement) section created by the Xaconnection database connection (different JTA providers have different implementations, where the code example just shows you how the transaction resources are automatically added to the transaction). We take the Execute method of the Session object as an example, by adding a call to the Associatewithtransactionifnecessary method at the beginning of the method, which guarantees that the operation of any database connection will be transparently added to the transaction during the JTA transaction.

Listing 9. Automatically associate transaction resources to transaction objects-Xastatement implements Statement
public void execute (String sql) {//For each database operation, check that the database connection in which this session resides has been added to the transaction Associatewithtransactionifnecessar  Y (); try{//Handling database operation code ....} catch (SQLException Sqle) {//Handling exception code} catch (Exception ne) {e.print  StackTrace (); }} public void Associatewithtransactionifnecessary () {//get TransactionManager TransactionManager TM = Gettransa                 Ctionmanager (); Transaction tx = Tm.gettransaction ();//Check whether the current thread has a distributed transaction if (tx! = NULL) {//within a distributed transaction, the TX object determines whether the current data connection is already contained  In the transaction,//If the connection is not then joined to the transaction Connection conn = This.getconnection (); Tx.hascurrentresource, Xaconn.getdatasource () is not a standard JTA//interface method, but a custom method added to implement distributed transactions if (!tx.hascu      Rrentresource (conn)) {Xaconnection Xaconn = (xaconnection) conn;      Xadatasource Xasource = Xaconn.getdatasource (); Call Transaction's interface method to join the database transaction resource into the current transactionTx.enlistresource (Xasource.getxaresource (), 1);}          }         }

XAResource and Xid:xaresource are Java implementations of the distributed Transaction Processing:the XA specification Standard, which is an abstraction of the underlying transaction resource, which defines the distributed transaction The protocol between the transaction manager and the resource manager in the process, and each transaction resource provider (such as JDBC driver, JMS) will provide an implementation of this interface. Using this interface, developers can implement distributed transactions through their own programming, but these are usually implemented by the application server (the server comes with more efficient and stable) to illustrate, we will illustrate how he uses it.
Before using distributed transactions, in order to differentiate transactions from confusion, you must implement a Xid class to identify transactions, you can think of Xid as a token of a transaction, each time a new transaction is created, a transaction is assigned a XID,XID containing three elements: FormatID, Gtrid (global transaction Identifier) and bqual (branch modifier word identifier). FormatID is usually zero, which means that you will use OSI CCR (Open Systems interconnection commitment, Concurrency and Recovery standards) to name it, and if you want to use a different format, the for The Matid should be greater than 0, and a value of -1 means that Xid is invalid.

Gtrid and Bqual contain 64-byte binaries to identify both global and branch transactions, with the only requirement that Gtrid and bqual must be globally unique.
The following methods are mainly defined in the XAResource interface:

    • commit ()-Commit Transaction
    • issamerm (XAResource xares)-Checks whether the current XAResource and parameters are the same transaction resource
    • prepare ()-notifies the resource manager to prepare a transaction for submission work
    • rollback ()-notifies the resource manager to roll back a transaction

When a transaction is committed, the Transaction object collects all the XAResource resources that are contained by the current transaction, and then invokes the resource's commit method, as shown in the following code:

Listing 10. Commit TRANSACTION-Transactionimpl implements Transaction
public void Commit () throws Rollbackexception, Heuristicmixedexception,  heuristicrollbackexception, SecurityException,  illegalstateexception, SystemException {  //Get all transaction resources in the current transaction        list<xaresource> List = Getallenlistedresouces ();  Notifies all transactional resource managers that they are ready to commit the transaction                        //For production-level implementations where additional processing is required to handle exceptions that occur during some resource preparation for (XAResource xa:list) {  xa.prepare ();  }  //All transactional resources, commit transaction for (XAResource xa:list) {  xa.commit ();}    }

Back to top of page

Conclusion

The example code in this article is a hypothetical implementation of the ideal scenario, given that the reader is aware of the principles of JTA. A well-established JTA transaction implementation needs to be considered with a lot of details, such as performance (when committing a transaction to commit a transaction using multiple threads), fault tolerance (network, System exception), and so on, its maturity needs to be accumulated over a long period of time. Interested readers can read some open source JTA implementations for further in-depth learning.

Reference Learning
    • Java official website "Java Transaction API Specification" provides detailed JTA information, including version, specification, etc., interested readers can refer to
    • This article "Java Transaction API Overview" (programming China) describes the use of XA resources in detail, and interested readers can refer to the learning
    • "Jotm" is an open source JTA implementation, the reader can refer to its source code in-depth learning
    • DeveloperWorks Java Technology Zone: Here are hundreds of articles on various aspects of Java programming.
Discuss
    • Participate in forum discussions.
    • Join DeveloperWorks Chinese community. View developer-driven blogs, forums, groups, and wikis, and communicate with other DeveloperWorks users.

JTA Deep Adventures-Principles and implementation

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.