Tuesday, November 19, 2013

Don't swallow Hibernate exceptions!

Every once in a while, I see a code pattern that makes me go "hmmm". Once instance of this is code that catches a Hibernate exception within a transaction and swallows it before doing some compensating action. Normally, the usual pattern is that if Hibernate throws an exception, you let it propagate past the transaction boundary, rolling back the transaction, before handling the error. You violate the established practice at your peril.

What peril? Well, Hibernate's reference manual says the following (section 13.2.3 on Exception Handling):

If the Session throws an exception, including any SQLException, immediately rollback the database transaction, call Session.close() and discard the Session instance. Certain methods of Session will not leave the session in a consistent state. No exception thrown by Hibernate can be treated as recoverable.

I have emphasized the relevant text in bold. If you're catching the exception to do some other action within the same transaction, there is a chance you're corrupting data. If you're lucky, at some point the transaction got marked rollback-only and Hibernate will not let you commit the mischief. But the Hibernate folks are saying that all bets are off if you swallow that exception.

If you think about it, this sort of pattern makes no sense in a transactional setting. When you demarcate an operation as being transactional, you are saying "this operation either succeeds entirely, or it fails entirely as if it never happened". Atomicity is a guarantee of a transaction. So if Hibernate throws an exception it means the operation has failed, and you're mucking with the meaning of a transaction if you attempt a compensating action. If you want to log an error, retry or otherwise record a failure, you are free to do it outside of that transaction.