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(); }
Comments