Mechanism: @Transactional is applied through a proxy (AOP). When a call enters the proxy, Spring opens or joins a transaction, runs the method, then commits, or rolls back on an exception.
Key attributes:
- propagation: REQUIRED (default, join or create), REQUIRES_NEW (suspend and start a new one, for example audit logging), NESTED (savepoints).
- isolation: READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE; default follows the database.
- readOnly: hint for optimisations on queries.
- rollbackFor: by default only unchecked exceptions (RuntimeException, Error) trigger rollback; checked exceptions commit unless configured.
Common pitfalls:
- Self-invocation: calling a @Transactional method from another method of the same class bypasses the proxy, so no transaction starts. Move it to another bean or use the transactional method as the entry point.
- Private or final methods: proxies cannot intercept them.
- Catching the exception inside the method: the transaction commits because nothing propagates.
- Checked exceptions not rolling back without rollbackFor.
- Long transactions holding locks, especially with remote calls inside them.
- Lazy loading outside a transaction causing LazyInitializationException.