views:

667

answers:

3

I am developing an EJB application to run on glassfish v3. If I look at the javadoc for the EntityManager class it says that methods like find, persist etc throw exceptions derived from PersistenceException. However, in practice I notice that exceptions derived from org.eclipse.persistence.exceptions.DatabaseException can be thrown if something goes wrong at the database level (a table can't be found for example). So am right in assuming that in addition to the standard persistence exceptions, I also have to handle exceptions thrown by whichever persistence provider I am using? That would tend to imply that I need to write error code specific to the JPA provider I choose and if I change to a different one later I need to change my code to catch a different exception class such as HibernateException.

+2  A: 

In my opinion, you should only handle exceptions from the standard JPA exception hierarchy (unless you want to deal with a particular case for which the specification doesn't have a standard exception in which case your application won't be portable - but I can't think of any out of my head). The EJB 3.0 JPA spec (JSR 220) summarize them in the section 3.7:

3.7 Summary of Exceptions

The following is a summary of the exceptions defined by this specification:

PersistenceException

The PersistenceException is thrown by the persistence provider when a problem occurs. It may be thrown to report that the invoked operation could not complete because of an unexpected error (e.g., failure of the persistence provider to open a database connection).
All other exceptions defined by this specification are subclasses of the PersistenceException. All instances of PersistenceException except for instances of NoResultException and NonUniqueResultException will cause the current transaction, if one is active, to be marked for rollback.

TransactionRequiredException

The TransactionRequiredException is thrown by the persistence provider when a transaction is required but is not active.

OptimisticLockException

The OptimisticLockException is thrown by the persistence provider when an optimistic locking conflict occurs. This exception may be thrown as part of an API call, at flush, or at commit time. The current transaction, if one is active, will be marked for rollback.

RollbackException

The RollbackException is thrown by the persistence provider when EntityTransaction.commit fails.

EntityExistsException

The EntityExistsException may thrown by the persistence provider when the per- sist operation is invoked and the entity already exists. The EntityExistsException may be thrown when the persist operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at commit time.

EntityNotFoundException

The EntityNotFoundException is thrown by the persistence provider when an entity reference obtained by getReference is accessed but the entity does not exist. It is also thrown by the refresh operation when the entity no longer exists in the database. The current transaction, if one is active, will be marked for rollback.

NoResultException

The NoResultException is thrown by the persistence provider when Query.getSingleResult is invoked and there is no result to return. This exception will not cause the current transaction, if one is active, to be marked for roll back.

NonUniqueResultException

The NonUniqueResultException is thrown by the persistence provider when Query.getSingleResult is invoked and there is more than one result from the query. This exception will not cause the current transaction, if one is active, to be marked for roll back.

For me, provider-specific exceptions are "internal" stuff most of time used to indicate technical problems i.e. bugs in your application that should be fixed (e.g. if a table is missing, it's a bug, fix it, it doesn't make sense to handle this kind of exception).

Pascal Thivent
Shane
@Shane I still think that EclipseLink exceptions (http://www.eclipse.org/eclipselink/api/1.0/org/eclipse/persistence/exceptions/EclipseLinkException.html) are INTERNAL (have a look at the javadoc), you should handle standard JPA exceptions, not provider-specific exceptions (which are not JPA exceptions strictly speaking).
Pascal Thivent
An application should never be thrown provider-specific exceptions. That is the whole point of having a persistence spec, so there is a well-defined outcome. Feel free to raise anything like that as a bug in the JPA implementation. Any provider-specific exception should be nested under the JPA exception, and you just catch the JPA variant
DataNucleus
@DataNucleus Thanks for this confirmation, I was actually very surprised by the question (and EclipseLink behavior).
Pascal Thivent
A: 

This is generally why the Spring framework translates implementation-specific exceptions into implementation-independent exceptions. If you are worried about the coupling of catching "internal" exception types you can write exception proxying code in your persistence layer to catch DatabaseException and rethrow your own ShanesPersistenceException extends RuntimeException (or whatever) with the original exception included as the cause. Then if you switch implementations later on you only have to touch the exception proxying to add a catch-and-rethrow for the new vendor's exception.

Josh McFadden
So I guess what you are suggesting is to write a wrapper for EntityManager which perfroms the exception translation, right?
Shane
Josh McFadden
One of the reasons of having a JPA spec is to do away with having Spring do any translation. All situations and exceptions are already defined. The problem is in EclipseLink throwing something illegal for the JPA contract
DataNucleus
snowflake
A: 

I did some more experimentation with this. When I switched to Hibernate I found that if I catch the exception in the bean that is calling the EntityManager method as a Throwable then if something happens which is outside of the cases supported by the PersistenceException subclasses (such as a table is missing) then the HibernateException gets wrapped in a plain Persistence Exception, which makes sense. If you want to know if something went wrong with persistence you can just catch PersistenceException.

In the TopLink case I receive the Eclipse DatabaseException directly. This seems like a bug to me - as mentioned by Pascal there should be no need to catch provider specific exceptions. I will make a bug report for Glassfish and post a link to the result here.

In both cases if the exception is not caught at the site of EntityManager call the exception will be caught and rethrown as something more general by the container such as EJBException or TransactionRolledBackException, and it may make more sense to catch these in most cases.

Shane