WhatIsTheCorrectPatternForUserTransactions

What is the correct pattern for UserTransaction demarcation

The correct pattern for working with UserTransactions in the server

CMT

 

The simple answer is use CMT. It is very seldom that you need BMT. You are less likely to have errors if you invoke CMT bean(s) with RequiresNew transaction demarcation.

 

But I need to change the transaction timeout

 

Different options about how to change the transaction timeout are explained in TransactionTimeout

 

The correct pattern for working with UserTransactions in the server

 

Common mistakes are to leave paths through the code where neither commit or rollback is invoked. These paths usually happen when you get an unexpected error.

 

  • Put the commit/rollback in a finally block

  • Catch and rethrow errors where you want to force a rollback

  • You might want to log the errors or map them to some other error

  • You might want to log all rollbacks?

 

UserTransaction ut = ... // e.g. new InitialContext().lookup("java:comp/UserTransaction");
ut.begin();
try
{
// DO SOMETHING
}
catch (Exception e)
{
   ut.setRollbackOnly(); // Force a rollback for this error
   throw e;
}
finally
{
   if (ut.getStatus() == Status.STATUS_ACTIVE)
      ut.commit();
   else
      ut.rollback();
}

 

The correct pattern for working with UserTransactions from remote clients

 

Common mistakes, again, are to leave paths through the code where neither commit or rollback is invoked but in the case of remote clients, we need to take into account that calls, such as getStatus(), can fail due to connectivity issues. It would be up to the client to decide whether it wants to wait and retry the call first before issuing a rollback() to clear the broken transaction but the important thing is that commit() or rollback() need to be called at the end of transaction, regardless of the outcome.

 

Assuming that the client did not attempt any retries, the following would be a valid pattern for remote clients:

 

UserTransaction ut = ... // e.g. new InitialContext().lookup("java:comp/UserTransaction");
ut.begin();
try
{
// DO SOMETHING
}
catch (Exception e)
{
   ut.setRollbackOnly(); // Force a rollback for this error
   throw e;
}
finally
{
  int status = -1;
  try
  {
    status = tx.getStatus();
  }
  catch(Exception e)
  {
    // log exception calling getStatus()
  }
            
  if (status == Status.STATUS_ACTIVE)
    tx.commit();
  else
    tx.rollback();
}