bmurauer
bmurauer

Reputation: 1289

JavaEE EJB Can't catch TransactionRolledbackException

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

Answers (4)

bmurauer
bmurauer

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

user1925885
user1925885

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

lcestari
lcestari

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

James
James

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

Related Questions