Using transactional and non-transactional Resources simultaneously
Problem:
- Bean performs operations on transactional Resource, for instance database connection, and on non-transactional Resource, for instance LDAP connection
- Bean exposes a business method that uses both database connection and LDAP connection
- Operations on database connection and LDAP connection must both succeed or be rolled back
What is the bast way to achieve it?
Transactional Resource is a transactional Managed Resource i.e. it integrates with JTA transactions. Non-transactional Resource is any Resource that does not support transactions or supports only local transactions.
Case 1. The business method can use isolated transaction. I.e. client’s transaction does not propagate and the business method is not a part of a larger transactional process.
Solution:
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class ABean {
@Resource
private UserTransaction tx;
...
public void businessMethod(){
//initialization
dbConnection = ...;
ldapConnection = ...;
tx.begin();
try {
//database operations in an abbreviation
dbConnection.executeQuery(...);
} catch (SQLException e) {
tx.rollback();
throw new RuntimeException(e);
}
try {
//LDAP operation in an abbreviation
ldapConnection.executeQuery(...);
} catch (LdapException e) {
tx.rollback();
throw new RuntimeException(e);
}
try {
//usually commit succeeds
//but if it fails then ldap operation should be rolled back
tx.commit();
} catch (Exception e) {
//rollback of LDAP operation
//is just a performance of the opposite operation
ldapConnection.executeQuery(...);
}
}
}
Description:
- It can be any type of Session Bean
- Exceptions handling is omitted or simplified, operations on connections are schematic and simplified, resources clean up is omitted, all for brevity
- Bean Transaction Management gives greater control over a transaction, if commit fails we can perform more non-transactional operation as a failure handling
- Usually commit rarely fails
Algorithm:
- Initialize connections
- Start a transaction
- Perform transactional database operations
- If any operation fails then just rollback the transaction
- Perform non-transactional LDAP operation
- If operation fails then just rollback the transaction
- Commit the transaction
- If commit fails then LDAP operation is rolled back manually by performing the opposite operation
Case 2. The business method cannot use isolated transaction. I.e. bean must be ready to propagate client’s transaction and the business method is intended to be a part of a larger transactional process.
We want to propagate a client’s transaction thus we cannot use Bean Transaction Management. The problem is handling of commit failure. If commit fails then LDAP operation can be rolled back only manually. If application does not require high consistency, i.e. excessive LDAP operations are accepted, then we can just ignore commit failure’s handling. Assume that an application must ensure high consistency. Then we need to control Container Managed Transaction. The only way to achieve it is to use Stateful Session Bean with transaction listening methods.
@Stateful public class ABean { @Resource private SessionContext ctx; ... public void businessMethod(){ try { //database operations in an abbreviation dbConnection.executeQuery(...); } catch (SQLException e) { ctx.setRollbackOnly(); return; } try { //LDAP operation in an abbreviation ldapConnection.executeQuery(...); } catch (LdapException e) { ctx.setRollbackOnly(); return; } } @AfterCompletion private void afterCompletion(boolean success) { if (success) return; //rollback of LDAP operation //is just a performance of the opposite operation ldapConnection.executeQuery(...); } }
Resources initialization and cleanup is omitted.