@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);
    }
}