Reputation: 1289
I am building a small JavaEE project and have a Repository Class:
@Stateless
public class UserRepository extends AbstractRepository<User> {
@PersistenceContext
private EntityManager em;
public User getByName(String username) {
CriteriaBuilder criteriaBuilder = ...
return em.createQuery(query).getSingleResult();
}
}
and i have Service class that uses this function:
@Stateless
public class UserService extends AbstractService<User> {
public User getByName(String username) {
try {
return userRepository.getByName(username);
} catch (Throwable e) {
// says: "Caught: EJBTransactionRolledbackException"
System.out.println("Caught: " + e.getClass());
return null;
}
}
}
I expect the catch (Throwable e)
clause to catch anything that might happen within this method, but apperently I'm wrong. I still get a TransactionRolledbackException
in my logs . I read Why can't I catch EJB exceptions? but there is no real answer to the question itself.
Why is the TransactionRolledbackException
still thrown and how can i prevent this? Also, what does it have to do with the EJBTransactionRolledbackException
that actually IS being caught?
I also tried catching the exception inside the getByName
method and throw a custom exception, with no luck though.
Upvotes: 2
Views: 2272
Reputation: 1289
With a little Help from Sameer Mali I found out why the transaction was being rolled back. The tracelog then could be avoided by throwing an exception that has the @ApplicationException
Annotation. Also, the Exception may not inherit from Throwable
, but must inherit from Exception
(which is only reasonable anyway). The code ended up like this:
public User getByName(String username) {
CriteriaBuilder criteriaBuilder = ...
try {
return em.createQuery(query).getSingleResult();
} catch (NoSuchResultException e){ // Note: this is a System Exception
throw new NoSuchUserException(username);
}
}
and
@ApplicationException
public class NoSuchUserException {
...
}
Upvotes: 0
Reputation:
As per your source code
UserService = EJB client
UserRepository = EJB being called by client/to be invoked EJB
As no Transaction attribute has been specified it defaults to REQUEIRED, thus transaction context is being propagated from UserService to UserRepository i.e. all the stuff is being executed in single transaction.
With some excerpts from EJB 3.1 specification page 386
Method condition: Bean method runs in the context of the caller’s transaction [Note A]. This case may happen with Required, Mandatory, and Supports attributes.
your condition satisfies this
Method exception: all other exceptions and errors
this basically means System exception is thrown which is your case (Application exception is another one in EJB, too much to explain)
most important part is following of container action
Container’s action
Log the exception or error [Note B].
Mark the transaction for rollback.
Discard instance [Note C].
Throw javax.ejb.EJBTransactionRolledbackException to client. [Note D]
Thus as per your source code UserService the client will recieve EJBTransactionRolledbackException Please read the specification for better understanding.
I hope this answers your question.
Also, just in case if you want to catch the exception add try-catch clause around em.createQuery(query).getSingleResult(); in UserRepository Class. This will also remove the exception tracelog you are getting.
Upvotes: 1
Reputation: 178
That happens due the default behavior of EJB is to use (http://docs.oracle.com/javaee/6/api/javax/ejb/TransactionAttributeType.html#REQUIRED) which will join all the invocation into a single transaction. Well, you can force to create another transaction using TransactionAttribute with the http://docs.oracle.com/javaee/6/api/javax/ejb/TransactionAttributeType.html#REQUIRES_NEW . It is good you take a very careful decision where you will start, join or start/new transaction (you can also force to have have a current transaction or make it not bound to any transaction (which can help for performance) ,for examples).
Regards, Luan
Upvotes: 0
Reputation: 18389
When you call one EJB method from another, by default it will join the same transaction. So the rollback will only occur in the outermost method. You could change the outer method to use BMT instead of CMT, then you will have control of the transaction commit and handling the error.
You could also make the inner call use a new transaction if you want it to be independent.
Upvotes: 0