Transactions in Java EE must be explicitly controlled, either using the UserTransaction
from JNDI or using deployment descriptor/annotations on EJBs. For a CDI component, here the UserController
, no transaction is started by default. (EDIT Transactions for EJB methods are enabled by default, if nothing is specified.)
So to start, your assumption:
the UserController.updateUser()
will execute userService.saveUser(user);
and logService.logEvent("Saved user " + fullName)
; within the same transaction
is wrong! I believe that a new transaction will be created and committed on each em.persist()
/em.merge()
call.
In order to wrap the calls saveUser()
and logEvent()
in the same transaction you can manually use the UserTransaction
:
public void updateUser(user) {
InitialContext ic = new InitialContext();
UserTransaction utx = (UserTransaction) ic.lookup("java:comp/UserTransaction");
utx.begin();
...
utx.commit(); // or rollback(), wrap them in try...finally
}
Or the friendlier:
@Resource UserTransaction utx;
...
public void updateUser(user) {
utx.begin();
...
utx.commit(); // or rollback(), wrap them in try...finally
}
Or even better the @Transactional
annotation, either with Java EE 7 or in Java EE 6 with the DeltaSpike JPA extension (or any other similar interceptor).
@Transactional
public void updateUser(user) {
...
}
You could specify that the EJB methods are transactional using the javax.ejb.TransactionAttribute
annotation. There would still be 2 transactions in this case. Alternatively you could move the "business" logic from the web-tier to the EJB-tier in a method annotated with @TransactionAttribute
and achieve running the DB methods in a single transaction.
As for further reading, check out the "Support for Transactions" chapter in the EJB 3 spec.