Spring Declarative Transactions
Spring declarative transactions let you define transaction boundaries with annotations such as `@Transactional` instead of managing `begin`, `commit`, and `rollback` manually. Spring typically applies this behavior through proxies around eligible method calls.
With `@Transactional`, Spring associates a method invocation with a database transaction. The transaction interceptor starts or joins a transaction before the method runs and then commits or rolls it back according to the configured rules.
By default, an unchecked exception such as `RuntimeException` causes rollback, while checked exceptions do not automatically trigger rollback. You can customize rollback behavior with attributes such as `rollbackFor`.
A common use is placing `@Transactional` on a service-layer method that performs several related database operations. If the method completes successfully, the changes can commit together; if an appropriate exception causes rollback, the transaction can undo the participating changes.
Spring's proxy-based transaction handling has an important consequence: a call from one method to another method on the same object can bypass the proxy. Therefore, self-invocation does not normally trigger transactional interception for the called method.
Transaction propagation controls what happens when a transactional method is called while another transaction already exists. The default `REQUIRED` behavior joins the existing transaction when one is present, or creates a new one otherwise.
Key points
- `@Transactional` defines transaction behavior declaratively.
- Spring commonly applies transaction interception through proxies.
- `RuntimeException` and `Error` trigger rollback by default; checked exceptions generally do not.
- `REQUIRED` joins an existing transaction or starts one when necessary.
- Self-invocation can bypass proxy-based transactional interception.
Interview questions
What does `@Transactional` do in Spring?
It tells Spring to apply transaction management around a method invocation. Spring typically does this through an interceptor associated with a proxy. The transaction is started or joined before the method executes and is completed afterward. Depending on the outcome and rollback rules, Spring commits or rolls back the transaction.
When does Spring roll back a transaction by default?
By default, Spring rolls back for unchecked exceptions, including `RuntimeException`, and for `Error`. Checked exceptions do not normally cause an automatic rollback. The rollback rules can be changed with attributes such as `rollbackFor`. The exact behavior should therefore be considered together with the declared exception types and transaction configuration.
What is `REQUIRED` propagation?
`REQUIRED` is the usual default propagation behavior. If the caller already has a transaction, the method participates in that transaction. If there is no existing transaction, Spring starts one for the method. Consequently, several participating operations can share the same transaction boundary.
Why might `@Transactional` not work when one method calls another method in the same class?
In Spring's usual proxy-based model, the transaction advice is applied when the call passes through the Spring proxy. A direct call such as `this.otherMethod()` stays inside the target object and can bypass that proxy. As a result, the annotations on the internally called method may not cause a new interception boundary. A common design is to place the transactional operation on a method invoked through another Spring-managed bean.
Where would you normally put `@Transactional` in a Java backend application?
A common choice is the service layer, where a method represents a business operation that may involve multiple data-access calls. This gives the transaction a boundary that can encompass the related work. Repository methods can still participate in that transaction without each needing to define an independent boundary. The appropriate location ultimately depends on the application's architecture and transaction requirements.