@Transactional Catch Block Without Rollback
ID |
java.hibernate_rollback_exception |
Severity |
low |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
Java |
Tags |
hibernate, orm, reliability |
Description
Reports @Transactional methods containing catch blocks that neither re-throw the exception nor call setRollbackOnly(). When an exception is caught silently inside a transactional method, the transaction commits partial changes, leading to data-integrity violations.
Rationale
Spring and Jakarta @Transactional only triggers a rollback when an uncaught exception propagates out of the method. If a catch block swallows the exception, the framework considers the method successful and commits the transaction — even if the business operation is incomplete.
// Bad -- exception swallowed, partial commit
@Transactional
public void transfer(Account from, Account to, int amount) {
try {
from.debit(amount);
to.credit(amount);
} catch (Exception e) {
log.error("Transfer failed", e);
// transaction still commits the debit!
}
}
Remediation
Re-throw the exception or explicitly mark the transaction for rollback:
// Good -- re-throw
@Transactional
public void transfer(Account from, Account to, int amount) {
try {
from.debit(amount);
to.credit(amount);
} catch (Exception e) {
log.error("Transfer failed", e);
throw e;
}
}
// Good -- setRollbackOnly
@Transactional
public void transfer(Account from, Account to, int amount) {
try {
from.debit(amount);
to.credit(amount);
} catch (Exception e) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
log.error("Transfer failed", e);
}
}