adam bien's blog

EJB: How To Catch javax.persistence.OptimisticLockException 📎

Exceptions like javax.persistence.OptimisticLockException may occur at commit time and so after the execution of an EJB method. Therefore some occurrences are impossible to catch with the convenient "Container Managed Transactions" configuration.

The method DataStore#update fails, because SomeEntity changes are going to be recognized at commit time and so after the execution of this method.


@Stateless
public class DataStore {

    @PersistenceContext
    EntityManager em;

    public void update(String id) {
        SomeEntity forUpdate = this.em.find(SomeEntity.class, id);
        forUpdate.makeDirty();
    }

}

The exception: "javax.ejb.EJBException: Transaction marked for rollback" is raised after the execution of the EJB and can be only caught in the presentation layer.

However, transactions can be started and committed, and so handled, in an interceptor:


public class TXEnforcer {

    @Resource
    UserTransaction tx;
    private final static Logger LOG = Logger.getLogger(TXEnforcer.class.getName());

    @AroundInvoke
    public Object beginAndCommit(InvocationContext ic) throws Exception {
        try {
            tx.begin();
            Object retVal = ic.proceed();
            tx.commit();
            return retVal;
        } catch (RollbackException e) {
            LOG.severe("-----------------Caught (in interceptor): " + e.getCause());
            throw e;
        } catch (RuntimeException e) {
            tx.rollback();
            throw e;
        }

    }
}

The EJB needs to use the TXEnforcer interceptor to handle transactions and has to switch to the "Bean Managed Transactions" strategy (otherwise you get a: Caused by: javax.naming.NameNotFoundException: Lookup of java:comp/UserTransaction not allowed for Container managed Transaction beans):


@Stateless
@Interceptors(TXEnforcer.class)
@TransactionManagement(TransactionManagementType.BEAN)
public class DataStore {
}

In this example the DataStore EJB is a boundary which always initiates a new transaction. Therefore there is no need to handle existing transactions.

Thanks Marian S. for asking the question!

[See also Boundary pattern in the "Real World Java EE Patterns--Rethinking Best Practices" book (Second Iteration, "Green Book"), page 57 in, chapter "Boundary"]

The project catchemall was checked in into http://kenai.com/projects/javaee-patterns

See you at Java EE Workshops at MUC Airport (Effective + Architectures -- usually transactions are heavily discussed at these days)!