The full picture
| Level | Dirty read | Non-repeatable read | Phantom |
|---|---|---|---|
| READ_UNCOMMITTED | possible | possible | possible |
| READ_COMMITTED | — | possible | possible |
| REPEATABLE_READ | — | — | possible (std) / mostly prevented in InnoDB |
| SERIALIZABLE | — | — | — |
- The anomaly that matters in practice: lost update / write skew under READ_COMMITTED — two transactions read a balance, both compute, last write wins. Isolation levels alone don't save you; you need optimistic versioning (
@Version), atomic UPDATE…WHERE, orSELECT FOR UPDATE. - MVCC intuition: each transaction sees a snapshot; writers create row versions instead of blocking readers — why 'readers block writers' folklore is wrong on Postgres/Oracle.
- Spring specifics:
@Transactional(isolation=…)applies per transaction (SET TRANSACTION); on a joined transaction the setting is ignored (or rejected with validateExistingTransaction). Also: isolation is a DB concept — don't confuse with propagation. - SERIALIZABLE operationally means retry loops: catch serialization failures (SQLState 40001) and re-run — infrastructure your service must own if you choose that level.