Java transaction learning notes (9)-in-depth analysis of JTA principles and implementation

Source: Internet
Author: User

Java transaction learning notes (9)-in-depth analysis of JTA principles and implementation
Through the study of java transactions in this series, I have a preliminary understanding of the concept of transactions. However, with the rapid development of the Internet, the data volume is growing explosively, common databases are increasingly becoming the performance bottleneck of application systems. distributed databases have emerged. Correspondingly, java Distributed Transaction JTA (Java Transaction API) has also emerged in this context. I have the honor to read the in-depth good articles of IBM ipvsworks, and share some of my understanding with the audience members for mutual communication and learning. Http://www.ibm.com/developerworks/cn/java/j-lo-jta/ uses JTA to process transactions

What is transaction processing?

Transactions are an indispensable component model in computer applications. They ensure the Atomicity, Consistency, Isolation, and Durabilily of user operations ). The most typical example of A transaction is credit card transfer: Transfers 500 yuan from user A's account to user B's account. The procedure is as follows:
1. Reduce the amount in account A by 500
2. Increase the amount in account B by 500
The two operations must ensure the transaction attribute of ACID: either all succeeded or all failed; if there is no transaction guarantee, the user's account amount may be faulty:
If the first step is successful and the second step fails, the amount of user A's account will be reduced by 500 yuan, but the amount of user B's account will not be increased (not available ); similarly, if the first step fails and the second step succeeds, the account amount of user A will remain unchanged, and the account of user B will increase by 500 yuan (born out of thin air ). Any of the above errors will cause serious data inconsistency. The lack of transactions is unacceptable for a stable production system.

J2EE transaction processing method

1. Local transactions: these transactions are closely dependent on the underlying resource manager (such as database connections), and the transaction processing is limited to the current transaction resource. This transaction processing method does not depend on the application server, so the deployment is flexible but cannot support distributed transactions with multiple data sources. An example of using a local transaction in a database connection is as follows:

List 1. Local transaction processing instance
Public void transferAccount () {Connection conn = null; Statement stmt = null; try {conn = getDataSource (). getConnection (); // set Automatic commit to false. // if it is set to true, the database identifies each data update as a transaction and automatically submits conn. setAutoCommit (false); stmt = conn. createStatement (); // reduce the amount in account A by 500 stmt.exe cute ("\ update t_account set amount = amount-500 where account_id = 'A '"); // increase the amount in account B by 500 stmt.exe cute ("\ update t_account set amount = amount + 500 where account_id = 'B'"); // submit the transaction conn. commit (); // transaction commit: the two-step transfer operation is successful at the same time} catch (SQLException sqle) {try {// exception occurs, and the rollback operation in this transaction is conn. rollback (); // transaction rollback: the two-step operation of the transfer completely revokes stmt. close (); conn. close ();} catch (Exception ignore) {} sqle. printStackTrace ();}}

2. Distributed Transaction Processing: Java Transaction Programming Interface (JTA: Java Transaction API) and Java Transaction Service (JTS; Java Transaction Service) provide distributed Transaction services for the J2EE platform. Distributed Transaction includes the Transaction Manager and one or more Resource managers that support XA protocol ). We can regard resource manager as any type of persistent data storage, and Transaction Manager coordinates and controls all transaction participants. The JTA transaction effectively shields the underlying transaction resources so that the application can participate in the transaction processing in a transparent manner. However, compared with local transactions, the XA Protocol has a high system overhead, during system development, you should carefully consider whether distributed transactions are required. If distributed transactions are required to coordinate multiple transaction resources, implement and configure transaction resources that support the XA protocol, such as JMS and JDBC database connection pools. An example of using JTA to process transactions is as follows (Note: connA and connB are connections from different databases)
Public void transferAccount () {UserTransaction userTx = null; Connection connA = null; Statement TA = null; Connection connB = null; Statement stmtB = null; try {// get the Transaction management object userTx = (UserTransaction) getContext (). lookup ("\ java: comp/UserTransaction"); // obtain the database connection connA = getDataSourceA () from database (). getConnection (); // obtain the database connection connB = getDataSourceB () from database B (). getConnection (); // start the transaction userTx. begin (); // reduce the amount in account A by 500 stmtA = connA. createStatement (); ta.exe cute ("update t_account set amount = amount-500 where account_id = 'A'"); // increase the amount in account B by 500 stmtB = connB. createStatement (); stmtB.exe cute ("\ update t_account set amount = amount + 500 where account_id = 'B'"); // submit the transaction userTx. commit (); // transaction commit: the two-step transfer operation is successful at the same time (data in database A and database B is updated at the same time)} catch (SQLException sqle) {try {// an exception occurs, and the userTx is rolled back in this transaction. rollback (); // transaction rollback: the two-step transfer operation is completely canceled // (data updates in database A and database B are also revoked) 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 working mechanism: the code I write does not have any code that interacts with transaction resources (such as database connections), but my operations (Database updates) but it is actually included in the transaction. How does JTA achieve this transparency? To understand the implementation principles of JTA, you must first understand its architecture: it includes the Transaction Manager and one or more Resource managers that support XA protocol, we can regard resource manager as any type of persistent data storage; Transaction Manager is responsible for coordination and control of all transaction units involved. Based on the object-oriented differences, we can understand JTA's Transaction Manager and resource manager as two aspects: interfaces for developers (Transaction Manager) and the implementation interface (Resource Manager) for service providers ). The main part of the development interface is the UserTransaction object referenced in the preceding example. developers use this interface to implement distributed transactions in the information system; the implementation interface is used to standardize the transaction service provided by the provider (such as the database connection provider). It defines the transaction resource management function, so that JTA can implement collaborative communication between heterogeneous transaction resources. Taking databases as an example, IBM provides a database driver for implementing distributed transactions, and Oracle also provides a database driver for implementing distributed transactions. When both DB2 and Oracle databases are used for connection, JTA can implement distributed transactions based on the two transaction resources of the agreed interface coordinator. Based on the different implementations of Unified Specifications, JTA can coordinate and control the transaction resources of different databases or JMS vendors. The architecture is shown in:

Figure 1. JTA Architecture

Developers use the developer interface to support global transactions of applications. Providers (databases, JMS, etc.) provide the transaction resource management function according to the specifications of the provider interface. The transaction Manager (TransactionManager) maps application usage of distributed transactions to actual transaction resources and coordinates and controls transaction resources. Next, this article will introduce three main interfaces including UserTransaction, Transaction and TransactionManager and their defined methods.

The developer-oriented interface is UserTransaction (as shown in the preceding example). developers generally only use this interface to implement JTA transaction management. The following method is defined:

  • Begin ()-starts a distributed Transaction (TransactionManager creates a Transaction object in the background and associates the object to the current thread through ThreadLocale)
  • Commit ()-commit a transaction (in the background, TransactionManager extracts the transaction object from the current thread and submits the transaction represented by this object)
  • Rollback ()-rollback transaction (in the background, TransactionManager extracts the transaction object from the current thread and rolls back the transaction represented by this object)
  • GetStatus ()-return the Status of distributed transactions associated with the current thread (all transaction statuses are defined in the Status object. For more information, see the API Documentation)
  •  
  • SetRollbackOnly ()-the distributed transaction associated with the current thread will be rolled back.

    The provider-oriented implementation interface mainly involves two objects: TransactionManager and Transaction.

    Transaction represents a physical Transaction. In this case, the developer calls UserTransaction. when the begin () method is used, TransactionManager creates a Transaction object (marking the beginning of the Transaction) and associates the object with the current thread through ThreadLocale. The commit (), rollback (), and getStatus () methods in the UserTransaction interface are all finally delegated to the corresponding method of the Transaction class for execution. The Transaction interface defines the following methods:

    • Commit ()-coordinates different transaction resources to complete transaction commit together
    • Rollback ()-coordinates different transaction resources to complete transaction rollback together
    • SetRollbackOnly ()-the distributed transaction associated with the current thread will be rolled back.
    • GetStatus ()-returns the status of distributed transactions associated with the current thread.
    • EnListResource (XAResource xaRes, int flag)-add transaction resources to the current transaction (in the preceding example, during database A operations, the transaction resources it represents will be associated with the current transaction. Similarly, when you operate database B, the transaction resources it represents will also be associated with the current transaction)
    • DelistResourc (XAResource xaRes, int flag)-delete the transaction resource from the current transaction
    • RegisterSynchronization (Synchronization sync)-the callback interface, Hibernate, and other ORM tools all have their own transaction control mechanisms to ensure transactions, at the same time, they also need a callback mechanism to be notified when the transaction is completed to trigger some processing work, such as clearing the cache. This involves the Transaction callback interface registerSynchronization. The tool can inject the callback program into the transaction through this interface. After the transaction is successfully committed, the callback
    • The program will be activated.

      TransactionManager does not provide the actual transaction processing function. It serves as a bridge between user interfaces and implementation interfaces. The methods defined in TransactionManager are listed below. We can see that most of the Transaction methods in this interface are the same as UserTransaction and Transaction. When the developer calls UserTransaction. when the begin () method is used, TransactionManager creates a Transaction object (marking the beginning of the Transaction) and associates the object to the current thread through ThreadLocale; similarly, UserTransaction. commit () calls TransactionManager. commit (), the method will take the Transaction object Transaction from the current thread and commit the Transaction represented by this object, that is, call Transaction. commit ()

      • Begin ()-start transaction
      • Commit ()-Submit a transaction
      • Rollback ()-rollback transaction
      • GetStatus ()-returns the current transaction status
      • SetRollbackOnly ()
      • GetTransaction ()-returns the transaction associated with the current thread
      • SetTransactionTimeout (int seconds)-set the transaction timeout
      • Resume (Transaction tobj)-continue the Transaction associated with the current thread
      • Suspend ()-suspends the transaction associated with the current thread

        During system development, you will need to temporarily exclude transaction resources. In this case, you need to call the suspend () method to suspend the current transaction: any operations performed after this method will not be included in the transaction. After the non-transactional operation is completed, call resume () to continue the transaction (Note: to perform this operation, you need to obtain the TransactionManager object, the obtaining method is different on different J2EE application servers)
        The following describes the implementation principles of JTA through specific code. Lists the Java classes involved in the example implementation. UserTransactionImpl implements the UserTransaction interface, TransactionManagerImpl implements the TransactionManager interface, and TransactionImpl implements the Transaction interface.

        Figure 2. JTA implementation class diagram list 3. Start transaction-UserTransactionImpl implenments UserTransaction
        Public void begin () throws NotSupportedException, SystemException {// delegate the transaction start operation to TransactionManagerImpl. singleton (). begin ();}

        Listing 4. Start transaction-TransactionManagerImpl implements TransactionManager
        // Here, transactionHolder is used to associate the Transaction object represented by Transaction to the private static ThreadLocal on the thread.
               
                
        TransactionHolder = new ThreadLocal
                
                 
        (); // TransacationMananger must maintain a global object, so 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 xid = new XidImpl (); // create a transaction object and associate it with the 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. When it is created, it indicates that the Transaction has started, therefore, you do not need to define the begin () method.

        Listing 5. Commit a transaction-UserTransactionImpl implenments UserTransaction
        Public void commit () throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {// check whether it is a Roll back only transaction. if it is a Roll back transaction if (rollBackOnly) {rollback (); return;} else {// delegate the transaction commit operation to TransactionManagerImpl. singleton (). commit ();}}

        Listing 6. Commit a transaction-TransactionManagerImpl implenments TransactionManager
        Public void commit () throws RollbackException, commit, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {// gets the transaction associated with the current transaction and submits TransactionImpl tx = transactionHolder through its commit method. get (); tx. commit ();}

        Similarly, rollback, getStatus, and setRollbackOnly methods are implemented in the same way as commit. The UserTransaction object does not control transactions. All Transaction methods are passed to the actual Transaction Resource (Transaction object) through TransactionManager.
        The preceding example demonstrates the JTA transaction processing process. The following shows how transaction resources (database connections, JMS) are transparently added to JTA transactions. The first thing to note 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, the MySql database connection is obtained locally, any updates to MySql will not be automatically included in global transactions.

        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 the Transaction management object userTx = (UserTransaction) getContext (). lookup ("java: comp/UserTransaction"); // obtain the mySql database connection mySqlConnection = DriverManager locally. getConnection ("localhost: 1111"); // obtain the database connection from database B. getDataSourceB returns the data source connB = getDataSourceB () of the application server (). getConnection (); // start the transaction userTx. begin (); // reduce the amount in account A by 500 // mySqlConnection is A local database connection and will not be included in global transactions. createStatement (); mySqlStat.exe cute ("update t_account set amount = amount-500 where account_id = 'A'"); // connB is the database connection obtained from the application server, will be included in the global transaction stmtB = connB. createStatement (); stmtB.exe cute ("update t_account set amount = amount + 500 where account_id = 'B'"); // transaction commit: connB operations are committed, the mySqlConnection operation will not be submitted by userTx. commit ();} catch (SQLException sqle) {// handle Exception Code} catch (Exception ne) {e. printStackTrace ();}}

        Why does the database connection that must be obtained from a data source that supports transactions support distributed transactions? In fact, the Data Source supporting transactions is different from the common data source. It implements an additional XADataSource interface. We can simply think of XADataSource as a common data source (inherited from java. SQL. PooledConnection), but it adds the getXAResource method to support distributed transactions. In addition, the database Connection returned by XADataSource is different from the common Connection. In addition to implementing all functions defined by java. SQL. Connection, this Connection also implements the XAConnection interface. We can understand XAConnection as a common database connection that supports all JDBC-compliant database operations. The difference is that XAConnection adds support for distributed transactions. The following class diagram shows the relationships between these interfaces:

        Figure 3. Transaction Resource class diagram

        The database connection obtained by the application from the data source that supports distributed transactions is implemented by the XAConnection interface, and the session (Statement) created by the database connection also adds features to support distributed transactions, the following code is used:

        Listing 8. JTA transaction resource processing
        Public void transferAccount () {UserTransaction userTx = null; Connection conn = null; Statement stmt = null; try {// get the Transaction management object userTx = (UserTransaction) getContext (). lookup ("java: comp/UserTransaction"); // gets the database connection from the database. getDataSourceB returns the data source conn = getDataSourceB () that supports distributed transactions (). getConnection (); // The stmt of the session has been enhanced to support distributed transactions. stmt = conn. createStatement (); // start the transaction userTx. begin (); stmt.exe cute ("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) part created by the XAConnection database connection (different JTA providers will have different implementation methods, the sample code here only 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, we can ensure that during the JTA transaction, any database connection operations will be transparently added to the transaction.

        Listing 9. automatically associate transaction resources to the transaction object-XAStatement implements Statement
        Public void execute (String SQL) {// For each database operation, check whether the database connection of this session has been added to the transaction associateWithTransactionIfNecessary (); try {// code for processing database operations ....} catch (SQLException sqle) {// handle Exception Code} catch (Exception ne) {e. printStackTrace () ;}} public void associateWithTransactionIfNecessary () {// obtain TransactionManager tm = getTransactionManager (); Transaction tx = tm. getTransaction (); // check whether the current thread has distributed transactions If (tx! = Null) {// in a distributed transaction, use the tx object to determine whether the current data connection has been included in the transaction, // if not, add the Connection to the transaction. Connection conn = this. getConnection (); // tx. hasCurrentResource, xaConn. getDataSource () is not a standard JTA // interface method, It is a custom method added to implement distributed transactions if (! Tx. hasCurrentResource (conn) {XAConnection xaConn = (XAConnection) conn; XADataSource xaSource = xaConn. getDataSource (); // call the Transaction interface method to add the Database Transaction resource to the current Transaction tx. enListResource (xaSource. getXAResource (), 1 );}}}

        XAResource and Xid: XAResource are The Java Implementation of Distributed Transaction Processing: The XA Specification standard, which is The abstraction of underlying Transaction resources, defines the protocol between the Transaction Manager and the resource manager in the Distributed Transaction processing process. Each transaction resource provider (such as JDBC driver and JMS) will provide the implementation of this interface. With this interface, developers can implement distributed transaction processing through their own programming, but these are usually implemented by the Application Server (the server's built-in implementation is more efficient and stable) to illustrate, we will illustrate how to use it.
        Before using distributed transactions, an Xid class must be implemented to identify transactions so that they are not confused. You can think of Xid as a identifier of a transaction, each time a new transaction is created, an Xid is assigned to the transaction. The 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 want to use another format, the value of-1 indicates that the value of Xid is invalid.

        Gtrid and bqual contain 64 bytes of binary code to identify global transactions and branch transactions respectively. The only requirement is that gtrid and bqual must be globally unique.
        The XAResource interface mainly defines the following methods:

        • Commit ()-Submit a transaction
        • IsSameRM (XAResource xares)-check whether the current XAResource and parameters are the same transaction Resource
        • Prepare ()-notify the resource manager to prepare the transaction commit
        • Rollback ()-notifies the resource manager to roll back the transaction

          When a Transaction is committed, the Transaction object collects all the XAResource resources contained in the current Transaction, and then calls the resource submission method, as shown in the following code:

          Listing 10. Commit a Transaction-TransactionImpl implements Transaction
          Public void commit () throws RollbackException, HeuristicMixedException, HeuristicRollbackException, SecurityException, IllegalStateException, SystemException {// obtain the List of all transaction resources in the current transaction
                   
                    
          List = getAllEnlistedResouces (); // notify all transaction resource managers and prepare to submit transactions. // for production-level implementation, additional processing is required to handle exceptions during resource preparation. for (XAResource xa: list) {xa. prepare ();} // all transactional resources, submit the transaction for (XAResource xa: list) {xa. commit ();}}
                   
          Conclusion

          Based on the above introduction, I believe that the reader has some knowledge about the JTA principle. The sample code in this article is an ideal hypothetical implementation. A well-developed JTA transaction implementation requires a lot of consideration and processing details, such as performance (multi-threaded concurrent transaction commit when committing a transaction), fault tolerance (Network, system exception) it also requires a long period of accumulation. Interested readers can read some open-source JTA implementations for further study.


Related Article

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.