[← What @Transactional does](01-what-transactional-does.md) · [Index](../README.md) · [Rollback →](03-rollback.md) # 2. The seven propagation values Transcript: [`01-propagation.txt`](output/01-propagation.txt). Every row was produced by calling the method, not by reading the enum. | Propagation | Caller has a transaction | Caller has none | |---|---|---| | `REQUIRED` (default) | joins it | starts one | | `REQUIRES_NEW` | suspends it, starts its own | starts one | | `NESTED` | savepoint — **fails on JPA**, see [chapter 4](04-nested-and-jpa.md) | starts one | | `SUPPORTS` | joins it | runs with **no** transaction | | `NOT_SUPPORTED` | **suspends** it, runs with none | runs with none | | `MANDATORY` | joins it | `IllegalTransactionStateException` | | `NEVER` | `IllegalTransactionStateException` | runs with none | ## The measured version ``` PROPAGATION CALLER ACTIVE TRANSACTION NAME / OUTCOME ---------------------------------------------------------------------------- REQUIRED inside @Transactional True inTransaction REQUIRED no transaction True required REQUIRES_NEW inside @Transactional True requiresNew NESTED inside @Transactional -- NestedTransactionNotSupportedException SUPPORTS inside @Transactional True inTransaction SUPPORTS no transaction False supports NOT_SUPPORTED inside @Transactional False notSupported MANDATORY no transaction -- IllegalTransactionStateException NEVER inside @Transactional -- IllegalTransactionStateException ``` Read the **name** column. `REQUIRED` inside a transaction reports `inTransaction` — the caller's method — because it joined. `REQUIRES_NEW` reports `requiresNew` — its own — because it started a second physical transaction. ## Notes that matter in practice **`SUPPORTS` with no transaction is not "no writes".** The method still runs and still writes; the write just lands on an auto-commit connection with no rollback available. `SUPPORTS` is for read paths that do not care, and it is a poor default for anything that mutates. **`NOT_SUPPORTED` suspends, it does not merely decline.** The caller's transaction is set aside and restored afterwards. Suspension holds the outer connection open while the inner work runs. **`REQUIRES_NEW` needs two connections at once.** The outer transaction keeps its connection while the inner one takes another. A pool sized to the number of request threads will deadlock under load — size it to exceed concurrent threads by at least one, per request nesting level. **`MANDATORY` is an assertion.** Use it on a helper that must never be called outside a transaction; it turns a silent correctness bug into a startup-visible exception. **`NEVER` is rare** and usually means the work should be somewhere else entirely. ## Exact messages ``` NESTED NestedTransactionNotSupportedException: Transaction manager does not allow nested transactions by default - specify 'nestedTransactionAllowed' property with value 'true' MANDATORY IllegalTransactionStateException: No existing transaction found for transaction marked with propagation 'mandatory' NEVER IllegalTransactionStateException: Existing transaction found for transaction marked with propagation 'never' ```