I am fine with having isRecoverable() on a DBI exception.
But if the rollback turns an otherwise recoverable exception into a non-recoverable one, isRecoverable() will still return false and I still can't retry.
Currently I solved my problem by overriding LocalTransactionHandler and providing my own tryRollback(handle) method. I would have preferred to not have to copy code from LocalTransactionHandler into my own method though.
/**
* Overrides the JDBI LocalTransactionHandler so that rollback methods called in response to other exceptions are not
* allowed to throw their own exceptions and thus mask the original exception that causes the rollback.
*/
public class RollbackSafeTransactionHandler extends LocalTransactionHandler
{
protected final Logger log = LoggerFactory.getLogger(getClass());
@Override
public <ReturnType> ReturnType inTransaction(Handle handle, TransactionCallback<ReturnType> callback)
{
final AtomicBoolean failed = new AtomicBoolean(false);
TransactionStatus status = new TransactionStatus() {
@Override
public void setRollbackOnly() {
failed.set(true);
}
};
final ReturnType returnValue;
try {
handle.begin();
returnValue = callback.inTransaction(handle, status);
if (!failed.get()) {
handle.commit();
}
} catch (RuntimeException e) {
tryRollback(handle); // instead of handle.rollback()
throw e;
} catch (Exception e) {
tryRollback(handle);
throw new TransactionFailedException("Transaction failed do to exception being thrown " +
"from within the callback. See cause " +
"for the original exception.", e);
}
if (failed.get()) {
tryRollback(handle);
throw new TransactionFailedException("Transaction failed due to transaction status being set " +
"to rollback only.");
} else {
return returnValue;
}
}
protected void tryRollback(Handle handle)
{
try {
handle.rollback();
} catch (Exception re) {
log.error("could not roll back transaction: {}", re.toString(), re);
}
}
}