Card 03/ 07
Rollback Rules: Checked Exceptions Commit by Default
One exception rolls the save back and the other lets it commit, and the difference is checked or unchecked.
By default, @Transactional rolls back only when the method throws an unchecked exception, a RuntimeException or an Error. If it throws a checked exception (one the compiler makes callers handle), the transaction commits on the way out, keeping every write made before the throw.
Two methods each save a row and then throw. One save is rolled back; the other is still there afterwards:
@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");
}after UNCHECKED exception, row count = 0
after CHECKED exception, row count = 1The rule is Spring's convention, not the database's. Checked exceptions were historically treated as expected business outcomes that the caller would handle, and an expected outcome was not meant to undo work.
It is costly because it fails silently: a method that saves an order and then throws a checked PaymentFailedException leaves the order saved and unpaid, and nothing in the logs says that the transaction committed or why.
← → move through sections and cards