views:

350

answers:

4

Hi all,

I am using EJB3 on Glassfish using the default TopLink persistance manager. Within a Session Bean, when the persistence manager catches a DB exception, it marks the transaction to be rolled back, and throws an EJBException, in turn wrapping a RollbackException. Now I was expecting to be able to get the original jdbc exception out of the caused by exception of one of these exceptions, but it is not.

It is important that I do retrieve the original exception, as I need to report back to the users what the problem is, and to do this I need to analyse the SQL error codes.

Does anyone know if it is possible to get this information from Toplink? Or whether Hibernate makes it possible?

Thanks,

+1  A: 

Good question, Ant

I know you want to throw a database exception but when it occurs the application, in most of the time, is not able to restore its initial state or it does not know how to recover from it. So it should be handled as a runtime exception. Some problems in database exceptions includes

  • database connection failure
  • query is wrong
  • table or column does not exist

Above you see the application is not be able to restore its initial state. If you think it is possible restore its initial state so you should use a application exception. Client will get the same application exception thrown by your business method. If you want to be able to get the exact exception thrown by your business method you have two choices:

  • Use a business delegate pattern to access your EJB

As you know, runtime exception is wrapped by a EJBException, so you shold use something like

Let's suppose you have this Stateless session bean

@Stateless
public class BeanImpl implements Bean {

    public void doSomething() {

        try {
            // some code
        } catch(SomeException e) {
            throw new EJBException(e);
        }

    }        

}

So you wrap your session bean through a business delegate

public class BeamBusinessDelegate implements Bean {

    // your stateless session bean goes here
    private Bean bean;

    public BeamImpl() {
        InitialContext i = new InitialContext();

        bean = (Bean) i.lookup(<GLOBAL_JNDI_ADDRESS_OR_RELATIVE_ENVIRONMENT_NAMING_CONTEXT_ADDRESS>);
    }

    public void doSomething() {
        try {
            bean.doSomething()
        } catch(EJBException e) {
            throw e.getCause();
        }
    }
}

Or you can extends EJBException according to your needs

public class DatabaseException extends EJBException {

}

So in your business method

@Stateless
public class BeanImpl implements Bean {

    public void doSomething() {

        try {
            // some code
        } catch(SomeException e) {
            throw new DatabaseException();
        }

    }        

}

regards,

Arthur Ronald F D Garcia
Hi - thanks for the response. Your examples demonstrate pretty much exactly what we are already doing. I think you have misunderstood my issue. The problem is that Toplink, Glassfishes default persistence manager, doesn't include the original cause in the 'cause' field of the EJBException.So my question really is, is there a way of getting Toplink to include the cause properly, or does Hibernate do this any better?
Ant
Oh - and it also looks like it wasn't clear that it isn't my code that's throwing the exception - we are using Entity Beans to persist the data, and it is the persistence manager throwing the EJBException which wraps a RolledbackException which does not have its cause set.
Ant
Ant, JPA is a higher-level abstraction above JDBC. I think you can not access underlying JBDC layer. After seeing PersistenceException - http://www.oracle.com/technology/products/ias/toplink/jpa/resources/javadoc/javax/persistence/PersistenceException.html - root exception of Java Persisntece API, it does not provides you a way in which you can access underlying JDBC layer, so i think it is not possible.
Arthur Ronald F D Garcia
Thanks Arthur - ChristiaanP has a promising looking lead above which I am going to try. I can't believe that there is no way of getting this information, as SQL error codes are essential if you want to successfully handle or report on errors in a persist or merge.
Ant
+1  A: 

I had the same issue. I ended up using the AroundInvoke interceptor method , that way you can catch any exception on the server side , and extract whatever info you want to and wrap it to throw your own exception , and set the EjbContext to rollback the transaction.

I can provide you with an example if you don't come right.

ChristiaanP
I hadn't come across interceptors before, but have just done some reading around them, and it looks like the sort of thing I want. However, it looks like I can define Interceptors for business methods, and for lifecycle events, but neither of these seem to be quite what I want, since the methods I want to intercept are in the EntityManager. A few more pointers would be gratefully received!
Ant
+1  A: 

The only way I've found to do what I want, is to force the manager to write to the db using manager.flush(), and then catch the PersistenceException that that throws. I can then log the database error as I want, and throw an EJBException to force rollback. Leaving the container to do the flush seems to irretrievably lose any useful messages with TopLink.

Ant
I'm having the same issue, can you edit your answer with code sample?
Thierry-Dimitri Roy
+1  A: 

Hello,

I have the same question : how to get the SQL error message generated from JPA?

I haven't found the solution either but, I added this line in my persistence.xml

 <properties>
  <property name="toplink.logging.level" value="FINE" />
 </properties>

and now, I can see the sql commands issued.

Reference : http://www.jairrillo.com/blog/2008/09/04/introduction-to-jpa-part-1-getting-started/

Pierre C