Reputation: 33
Consider the following scenario :
Stateless Annotated Class ClassOne
@Stateless
public class ClassOne {
// some injected fields
// ....
@Inject
private ClassTwo classTwo;
// ....
public void methodInClassOne() {
try {
classTwo.methodInClassTwo();
} catch(Exception e) {
// handle exception
}
}
}
Stateless annotated Class ClassTwo
@Stateless
public class ClassTwo {
// some injected fields
// ....
@Inject
private ClassThree classThree;
// ....
public void methodInClassTwo {
try{
classThree.methodInClassThree();
} catch (Exception e) {
// handle exception
}
}
}
Non-annotated Class ClassThree
public class ClassThree {
// some injected fields
// ....
public void methodInClassThree {
// some business logic
// ....
if (conditionCheck) {
throw new RuntimeException("error message");
}
}
}
Say, for this case, the above conditionCheck always evaluates to true. This is a working code as of today. The RuntimeException is wrapped in an EjbException and caught, handled and rethrown as expected until it reaches ClassOne's catch block. But the moment I make ClassThree Stateless (with @Stateless), the RuntimeException caught up becomes an EjbTransactionRolledBackException causing the transaction to be Rolledback and any handling in ClassOne that tries to call a persist service blows up due to this. I tried experimenting with the @TransactionAttributes :
SUPPORTS, REQUIRED -> gives the same RollBack behaviour killing the transaction
NOT_SUPPORTED -> gives, even before the condition check, on a JpaRepository call a TransactionRequiredException (with which I assumed there should have been a TransactionType with the original non-annotated class as well. And probably not the same Transaction as in ClassTwo - due to point 1.)
REQUIRES_NEW -> which seemed to behave as the original code behaved.
I was under the impression that if nothing was explicitly stated then the invoked method/class would use the default type REQUIRED (which apparently wasn't the case because as mentioned in point 1.). So how does the TransactionType work between the Annotated(EJB)-NonAnnotated(CDI) beans? Is it different from how it works between two Annotated(EJB) beans? Im not sure if my question is clear. In short the whole Transaction behaviour is confusing, especially because of how it behaves differently for ClassThree before and after making it Stateless.
Any inputs or reference to more informations on this would be really helpful. Thanks in advance
Upvotes: 0
Views: 816
Reputation: 19445
When you add @Stateless
to ClassThree
it becomes implicitly transactional and (as you say) behaves with a transaction type of REQUIRED
).
Every EJB call passes through a notional "boundary" where the security credentials of the call are checked and a (new) transaction set up if required (amongst other things). The result of the call is passed back through the same boundary at the completion of the call.
By default*, if the result of the call is a java.lang.RuntimeException
then the container is required to
This all happens on the way back through that boundary.
Consequently, methodInClassTwo
catches an EJBTransactionRolledbackException
, a subclass of EJBException
. Note that it is step 1 that kills your transaction, rather than any subsequent exception handling.
If methodInClassThree
was marked up with a transaction type of REQUIRES_NEW
, then a new transaction is always created upon entry to the method boundary. Any previously existing transaction is suspended. Throwing a RuntimeException behaves as above, except that it is the new transaction that gets rolled back. The transaction that was active before entering the boundary remains intact and is resumed.
Finally, if methodInClassThree
was marked up with a transaction type of NOT_SUPPORTED
, the current transaction is suspended at the boundary, and resumed at call completion. There is no rollback due to a RuntimeException being thrown because there is no active transaction. But you don't want this scenario anyway.
* You can modify this behaviour using @javax.ejb.ApplicationException.
Upvotes: 0