views:

338

answers:

2

Hi All,

I am using Spring and Hibernate. And Atomikos for transactions. I use annotation based transactions. I am having a DAO object and in one of the methods I call entityManager.persist() to save the object. Now whenever there is an ORA- error during update, for example a constraint is violated or one of the columns is having length more than wat is defined in the database, I get a JTAUnexpectedRollbackException instead of the GenericJDBCException that is thrown by Spring. I tried putting a try around persist to catch that, but I am not getting any exception there. Seems Hibernate does the actual update statement during flush, which happens during the transaction commit and hence the UnexpectedRollbackException I guess.

How can I get around this and get the GenericJDBCException instead of the UnexpectedRollbackException?

+1  A: 

First of all, disclaimer: I don't use Atomikos. I don't believe it has any relation to the error in question, but can't know that for sure.

I've had a very similar problem a while ago in Spring / Hibernate application. Hibernate indeed only propagates session updates to the database during flush. The problem is that flush may take place at different times depending on flush mode. It defaults to AUTO which means flush may occur before query execution if session contains updates that may affect query results. You have several options here:

A) You can manually call entityManager.flush() immediately after calling entityManager.persist() and catch the exception (if any) at that point. The downside to this (which may or may not be applicable in your case) is that flush disrupts batch updates, so you may experience a (potentially) significant performance decrease if you do multiple inserts / updates for the same entity type within a single transaction.

B) You can set your flush mode to COMMIT which would delay flushes until transaction is committed (or until flush() is invoked manually). You can then catch the exception at that point OR, if that proves impossible due to Atomikos, you can manually invoke flush() just before commit (at the end of your service call, for example). The downside is that this is harder then option A (it was harder in my case, it may be a lot harder with Atomikos - I don't know) AND your queries may potentially return stale data.

ChssPly76
+1  A: 

If UnexpectedRollbackException contains the GenericJDBCException as it's cause or a couple of causes downstream, you can fetch it programmatically using getCause(), getRootCause() methods and re-throw it.

try {
    em.flush();
} catch (UnexpectedRollbackException e) {
    if (e.getRootCause() instanceof GenericJDBCException) {
        throw e.getRootCause();
    }
}

I am not sure if getRootCause() or getCause() [.getCause()] return GenericJDBCException, you should find it out, probably, with a debugger.

artemb