0
votes

I have a simple EJB stateless class with two methods, both without @TransactionAttribute. The first method saves beer and invokes another one which saves other beer and throws an exception.

@Stateless
public class EJBContainer {

    @PersistenceContext
    EntityManager em;

    public void testTransaction() {
        em.persist(getBeer("Corona"));
        try {
            saveAndThrowException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void saveAndThrowException() {
        em.persist(getBeer("Heineken"));
        throw new RuntimeException();
    }

    //getBeer() definition
}

I assumed that both methods would use default transaction type (required) - so a new transaction will be created in the first method and it will be used in the second method. So throwing an exception should rollback this transaction and no beers will be saved by EntityManager. But it turned out that both beers have been saved. Where am I wrong?

When I moved a second method to the other stateless EJB it worked as I had supposed - the first EJB created new transaction, the second used it, exception caused a rollback and no beers had been saved.

The EJB is called from this JAX-RS client:

@ApplicationScoped 
public class BeerController { 

    @Inject 
    private EJBContainer ejbContainer; 

    @POST 
    public void addBeer() { 
        ejbContainer.testTransaction(); 
    } 
}
2
Whats your client code?areus
It is called from a simple rest controller class: @ApplicationScoped public class BeerController { @Inject private EJBContainer ejbContainer; @POST public void addBeer() { ejbContainer.testTransaction(); } }Aleksander Ka

2 Answers

2
votes

As you are using a no interface view, may be the injection is not working correctly. I would try to use the @EJB annotation instead of @Inject.

@EJB 
private EJBContainer ejbContainer; 

@POST 
public void addBeer() { 
    ejbContainer.testTransaction(); 
}

This way you should get the expected results. But one of your assumptions is not exactly correct. When you call saveAndThrowException from testTransacion is not an EJB invocation, it's just a local method invocation, if your declared a different @TransactionAttribute for saveAndThrowException if would not matter.

If you need to call a transactional method from another transactional method of your EJB you can do it adding a reference to the EJB itself, and call it like you would do if it was another EJB:

@Stateless
public class EJBContainer {

    @PersistenceContext
    EntityManager em;

    @EJB
    private EJBContainer ejbContainer;

    public void testTransaction() {
        em.persist(getBeer("Corona"));
        try {
            ejbContainer.saveAndThrowException();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void saveAndThrowException() {
        em.persist(getBeer("Heineken"));
        throw new RuntimeException();
    }

    //getBeer() definition
}
2
votes

Can't comment yet, not enough reputation. Therefore a separate post.

Areus has the point. The way you call it is just a local invocation, therefore all the interceptors around saveAndThrowException which are executed when you call it from outside are omitted by the application server. You throw the exception and directly catch it before returning control to the application server. The application server does not know anything about the exception, ergo erverything is fine and the transaction gets commited.