Using TM API to suspend and resume a JTA transaction to allow JDBC Commits

Assume your project has the following JPA transaction requirements

  • Start long a  running JTA transaction
  • Within the same thread which starts the JTA transaction you need to integrate a JAVA package which runs JDBC initiated COMMITs
  • The transaction outcome of this local transaction should be independent from our global JTA traansaction

Test Environment

12:09:59.294 Hibernate Version: 4.3.7.Final
12:09:59.294 Driver Name             : Oracle JDBC driver
12:09:59.294 Driver Version          : 12.1.0.2.0
12:09:59.294 Database Product Version: Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
             With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
             Advanced Analytics and Real Application Testing options
12:10:01.448 DB Name:  BANKA
12:10:01.449 1. Instance Name: bankA_1 - Host: hract21.example.com - Pooled XA Connections: 34
12:10:01.449 2. Instance Name: bankA_3 - Host: hract22.example.com - Pooled XA Connections: 33

Call Flow using UserTransaction API [ not working ]

Global JPA transaction:
  ut  = (UserTransaction)new InitialContext().lookup("java:comp/UserTransaction");
  ut.begin();
  em=getEntityManager();
  em.joinTransaction();
  em.persist(e);
  em.flush();                                // Insert record into our RAC DB 
  em.merge(e);                               // Update new record

Start local transaction which is independent from global transaction !!
  Connection c1 = ds1.getConnection();
  preparedStmt.executeUpdate();
  c1.commit(); 
-> Local Commit fails with following error 
Error 
11:13:57.087 Error in writelog()
11:13:57.090 You cannot commit during a managed transaction!
11:13:57.091 java.sql.SQLException: You cannot commit during a managed transaction!
    at org.jboss.jca.adapters.jdbc.BaseWrapperManagedConnection.jdbcCommit(BaseWrapperManagedConnection.java:1065)
    at org.jboss.jca.adapters.jdbc.WrappedConnection.commit(WrappedConnection.java:758)
    at com.hhu.wfjpa2el.JPATestBean.writeLog(JPATestBean.java:1729)
    at com.hhu.wfjpa2el.JPATestBean.increaseSalary(JPATestBean.java:1221)
    at com.hhu.wfjpa2el.JPATestBean.testSuspend(JPATestBean.java:630)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

-> Your are not allowed to call commit() or rollback() when a JTA transaction is ACTIVE !

Fix : Use the JEE 6 introduced TM API and suspend/resume the JTA transaction

Call Flow
  tm = (TransactionManager)ctx.lookup("java:/TransactionManager"); 
  tm.setTransactionTimeout(txTimeout);
  tm.begin();
  em=getEntityManager();
  em.joinTransaction();
  em.persist(e);
  em.flush();                                // Insert record into our RAC DB 
  em.merge(e);                               // Update new record
  tx = tm.suspend();                         // Save the transaction handle - we need to resume the transaction again
-> Global Transaction is suspended now

Start local transaction which is independent from global transaction !!
  Connection c1 = ds1.getConnection();
  c1.setAutoCommit(false);
  preparedStmt.executeUpdate();
  c1.commit();
-> Local Transaction is commited 

Resume global JTA transaction
 tm.resume(tx);
 em.merge(e);
 tm.commit();
-> Global JTA transaction is now commited 

Working Log

 
2:14:31.826 Calling increaseSalary() in progress - ID: 9997 - isUseSuspend : true
12:14:31.826  ------- Testing Suspend/Resume Operation ----------
12:14:31.826 Lookup Transactionmanager: ctx.lookup(java:/TransactionManager) - Tx Timeout: 120
12:14:31.827 Begin Transaction: tm.begin() - Before [TM status:  6 - STATUS_NO_TRANSACTION] - After [TM status:  0 - STATUS_ACTIVE]
12:14:41.569 checkEntity() -  :  empno: 1000001 - Record not yet found in Database !
12:14:41.569 :: After First Salary Change - Old Sal : 1000 - Salary Increase I : 500 - Current Salary: 1500
12:14:41.570 Suspended Transaction: tm.suspend() - Before [TM status:  0 - STATUS_ACTIVE] - After [TM status:  6 - STATUS_NO_TRANSACTION]
12:14:41.570 writelog():: Message: 12:14:41.570 :: After First Salary Change - Old Sal : 1000 - Salary Increase I : 500 - Current Salary: 1500 - ENO: 1000001
12:14:41.624 Leaving writelog() without Exceptions - Data commited !
12:14:41.624 Resumed Transaction: tm.resume(transaction)  - Before [TM status:  6 - STATUS_NO_TRANSACTION] - After [TM status:  0 - STATUS_ACTIVE]
12:14:41.692 :: Commit Tx: tm.commit() done   - Before [TM status:  0 - STATUS_ACTIVE] - After [TM status:  6 - STATUS_NO_TRANSACTION]
12:14:41.692 writelog():: Message: 12:14:41.692 :: 2nd Salary Change - Old Sal : 1000 - Salary Increase II : 99 - Current Salary: 1599 Need to Rollback : false - ENO: 1000001
12:14:41.779 Leaving writelog() without Exceptions - Data commited !
12:14:41.780 writelog():: Message: 12:14:41.780 :: Commit Tx: tm.commit() done   - Before [TM status:  0 - STATUS_ACTIVE] - After [TM status:  6 - STATUS_NO_TRANSACTION] - ENO: 1000001
12:14:41.801 Leaving writelog() without Exceptions - Data commited !
12:14:41.801  ------- Clear an reread Entity after TX completion  ----------
12:14:41.848 checkEntity() -  :  empno: 1000001 - Sal: 1599 - Entity Salary Persistence : 1599 - Managed Entity Status : true - STALE Data: false
12:14:41.940 -->readLog(): [empno=1000001] - Found 3 Records
12:14:41.940            : LogID:     20 - 12:14:41.570 :: After First Salary Change - Old Sal : 1000 - Salary Increase I : 500 - Current Salary: 1500 
12:14:41.940            : LogID:     21 - 12:14:41.692 :: 2nd Salary Change - Old Sal : 1000 - Salary Increase II : 99 - Current Salary: 1599 Need to Rollback : false 
12:14:41.940            : LogID:     22 - 12:14:41.780 :: Commit Tx: tm.commit() done   - Before [TM status:  0 - STATUS_ACTIVE] - After [TM status:  6 - STATUS_NO_TRANSACTION] 
12:14:41.940 Leaving increaseSalary() without Exceptions !
12:14:41.940 Leaving increaseSalary() - Entity manager  closed !

Leave a Reply

Your email address will not be published. Required fields are marked *