Transactional Annotation in Spring Boot
The @Transactional Annotation in Spring Boot: A Detailed Explanation
The @Transactional annotation in Spring Boot provides a declarative approach to handling
transactions. This is essential in systems that interact with databases, ensuring that a group of
operations either complete successfully or fail without affecting data integrity. Let's explore its
usage, configuration options, and the significance of each.
1. Automatic Rollback
When using @Transactional, Spring provides automatic rollback functionality for transactions. If an
exception occurs within a transaction, Spring rolls back the entire operation, ensuring no partial data
is persisted to the database.
Example:
@Transactional
public void updateUser(User user) {
userRepository.save(user);
if (someConditionFails()) {
throw new RuntimeException("Transaction should be rolled back.");
By default, Spring only rolls back on unchecked exceptions (i.e., instances of RuntimeException),
but you can configure it to rollback on other exceptions as well (explained later).
2. Transaction Propagation
Propagation defines how transactions behave when a method annotated with @Transactional is
called within another transactional method. Spring provides several propagation options:
- REQUIRED (default): Join the existing transaction if one exists, or create a new one if none exists.
- REQUIRES_NEW: Always create a new transaction. The existing one will be suspended until the
new transaction completes.
- NESTED: Executes within a nested transaction, meaning you can roll back the inner transaction
without affecting the outer transaction.
- SUPPORTS: Join the existing transaction if one exists, but if none exists, execute without a
transaction.
- NOT_SUPPORTED: Don?t execute in a transaction. If there is an existing transaction, it will be
suspended.
- MANDATORY: Requires an existing transaction. If none exists, it will throw an exception.
- NEVER: Ensures that the method never runs within a transaction. If a transaction exists, it will
throw an exception.
Example:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createNewOrder(Order order) {
orderRepository.save(order);
3. Transaction Isolation Levels
Isolation levels define how transactions interact with one another, particularly concerning data
consistency and visibility across transactions. The five isolation levels available in Spring are:
- DEFAULT: Uses the default isolation level of the underlying database.
- READ_UNCOMMITTED: Allows reading uncommitted changes from other transactions, which can
lead to dirty reads.
- READ_COMMITTED: Prevents dirty reads but allows non-repeatable reads (i.e., data can change
between reads in the same transaction).
- REPEATABLE_READ: Ensures the same data is returned if it?s read multiple times within a
transaction but can allow phantom reads (new data added during the transaction).
- SERIALIZABLE: The strictest isolation level. Transactions are executed sequentially, preventing
dirty reads, non-repeatable reads, and phantom reads.
Example:
@Transactional(isolation = Isolation.SERIALIZABLE)
public void processPayment(Payment payment) {
// Complex business logic
4. Read-Only Transactions
For operations that only retrieve data without modifying it, marking the transaction as read-only can
optimize performance. When readOnly=true is specified, Spring can optimize certain operations by
skipping unnecessary operations related to managing data changes.
Example:
@Transactional(readOnly = true)
public List<User> findAllUsers() {
return userRepository.findAll();
5. Timeout
The timeout attribute defines how long the transaction can run before it is automatically rolled back if
it?s not completed. This is useful for preventing long-running operations that could hold database
resources for too long.
Example:
@Transactional(timeout = 10) // 10 seconds
public void longRunningOperation() {
// Long-running logic that should fail if it exceeds 10 seconds
6. Rollback for Specific Exceptions
By default, Spring rolls back a transaction for unchecked exceptions (RuntimeException and its
subclasses). However, you can explicitly specify which exceptions should trigger a rollback and
which should not.
- rollbackFor: Specify which exceptions should trigger a rollback.
- noRollbackFor: Specify which exceptions should not trigger a rollback.
Example:
@Transactional(rollbackFor = Exception.class)
public void updateUserDetails(User user) throws Exception {
userRepository.save(user);
if (someErrorCondition()) {
throw new Exception("Force rollback for checked exception");
You can also use noRollbackFor to avoid rolling back for specific exceptions.
Example:
@Transactional(noRollbackFor = CustomException.class)
public void updateUser(User user) throws CustomException {
userRepository.save(user);
if (validationFails()) {
throw new CustomException("This will not trigger a rollback.");
7. Class-Level @Transactional
You can apply the @Transactional annotation at the class level, which will automatically apply the
same transactional behavior to all public methods within the class. This is useful when you want
consistent transactional behavior for all methods in a service.
Example:
@Service
@Transactional
public class UserService {
public void createUser(User user) {
userRepository.save(user);
public void updateUser(User user) {
userRepository.save(user);
In this case, both createUser and updateUser methods will be transactional without needing
individual @Transactional annotations.
Conclusion
The @Transactional annotation in Spring Boot is a powerful mechanism to manage transactions
declaratively. It provides a range of options such as transaction propagation, isolation levels,
timeout, and rollback policies. By understanding these options, you can fine-tune your application's
transactional behavior to ensure data consistency, integrity, and optimal performance.