Search code examples
jpatransactionsejb

Manually starting new transaction while another transaction is active


I want to do below steps while already in active transaction

  1. insert/update some records
  2. select all those records hold it in memory with non entity copy
  3. rollback first step without rolling back parent transaction
  4. return result from step 2.

I have this

@Stateless
public class DBWorker {

    public Result doWork(boolean isPreview) {
        Result result;

        if(isPreview) {
            em.getTransaction().begin();
        }
        doSomeWork();
        List<SomeEntity> resultOfDoSomeWork = em.createQuery(..).getResultList();
        result = copyToNonEntity(resultOfDoSomeWork);

        if(isPreview)
            em.getTransaction().rollback();

        return result;
    }
}

But gives exception A JTA EntityManager cannot use getTransaction() IllegalStateException:A JTA EntityManager cannot use getTransaction()


Solution

  • You have to annotate your EJB method with the @TransactionAttribute Annotation.

    In particular, you have to do something like this:

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void myMethod() {
       // do something
    }
    

    REQUIRES_NEW indicates that a new transaction has to be started for the business method.

    Pay attention to call the method to another EJB because if you call it from the same one the new transaction will not be created.

    ROLLBACK

    You can roll back the container-managed transaction:

    1. Throwing a RuntimeException: the container will automatically roll back the transaction.
    2. Throwing a custom Exception annotated by @ApplicationException(rollback=true):
    3. By invoking the setRollbackOnly method of the EJBContext interface: forces the container to rollback the transaction, also if there is no exception.

    For example:

    Throwing a RuntimeException:

    try {    
       // do something
    } catch(SQLException e) {
       throw new EJBException();    
    }
    

    Throwing a custom Exception:

    try {
       // do something
    } catch(SQLException e) {
       throw new MyCustomException();
    }
    

    where MyCustomException class is something like:

    @ApplicationException(rollback=true)
    public class MyCustomException extends Exception {
       // your custom methods
    }
    

    By invoking setRollbackOnly the method:

    Context.setRollbackOnly();