Suspend ()-suspends the transaction associated with the current threadDuring 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 UserTransactionPublic 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 UserTransactionPublic 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 TransactionManagerPublic 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 ProcessingPublic 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 diagramThe 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 processingPublic 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 StatementPublic 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 TransactionPublic 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 ();}}
ConclusionBased 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.