Card 03/ 07

GotchaDifficulty: Advanced1 min

The Exception That Doesn't Roll Back

Two methods, each saving a row and then throwing. One rolls the save back. The other doesn't — the row is still there afterward.

java
@Transactional
void insertThenThrowUnchecked() {
    repo.save(new LedgerEntity("unchecked-attempt"));
    throw new RuntimeException("boom - unchecked");
}

@Transactional
void insertThenThrowChecked() throws MyCheckedException {
    repo.save(new LedgerEntity("checked-attempt"));
    throw new MyCheckedException("boom - checked");
}
text
after UNCHECKED exception, row count = 0
after CHECKED exception, row count = 1
run in a container — the checked exception's insert survives

Spring's default rollback rule: an unchecked exception (RuntimeException or Error) rolls the transaction back; a checked exception does not, and the transaction commits on its way out. This traces back to Spring's own convention, not the database's — checked exceptions were historically treated as expected, recoverable outcomes, and an expected outcome doesn't undo work. Throwing a checked exception from inside a @Transactional method and assuming it behaves like any other failure is exactly how a half-finished operation quietly commits.