Spring Boot Transaction Management Concepts Questions and Answers — Questions and Answers
Question 1: An `OrderService` method, annotated with `@Transactional`, calls a public method in an `InventoryService` bean which is annotated with `@Transactional(propagation = Propagation.REQUIRES_NEW)`. If the `InventoryService` method completes successfully, but an unhandled exception is thrown later within the `OrderService` method, what will be the final state of the database changes?
- All changes from both services will be rolled back.
- Changes made by `InventoryService` will be committed, while changes made by `OrderService` will be rolled back. (Correct answer)
- All changes from both services will be committed because the exception was unhandled.
- The outcome is non-deterministic and depends on the database isolation level.
Correct answer: Changes made by `InventoryService` will be committed, while changes made by `OrderService` will be rolled back.
`Propagation.REQUIRES_NEW` starts a completely new, independent transaction. It suspends the outer transaction (from `OrderService`), executes, and then commits or rolls back on its own. Since the `InventoryService` method completed successfully, its transaction was committed. The subsequent failure in `OrderService` causes its own transaction to roll back, but this does not affect the already-committed independent transaction of `InventoryService`.
Question 2: What is the primary benefit of marking a transactional method with `@Transactional(readOnly = true)` in a Spring Boot application using JPA and Hibernate?
- It prevents the method from performing any write operations by throwing an exception if one is attempted.
- It automatically sets the transaction isolation level to `READ_UNCOMMITTED` for better performance.
- It signals to the underlying persistence provider to apply performance optimizations, such as disabling dirty checking. (Correct answer)
- It ensures that the method will not participate in any existing transaction and will always run non-transactionally.
Correct answer: It signals to the underlying persistence provider to apply performance optimizations, such as disabling dirty checking.
The main purpose of the `readOnly = true` flag is to serve as a hint for the persistence provider. For Hibernate, this allows it to apply significant optimizations, most notably skipping the costly dirty checking process (as it doesn't need to track changes) and not maintaining snapshots of entities. This reduces memory usage and CPU overhead, improving performance for read-heavy operations.
Question 3: In a Spring Boot application, if a public method in a `@Service` bean is annotated with just `@Transactional` and throws a `RuntimeException` during its execution, what is the expected default transactional behavior?
- The transaction will be committed because only checked exceptions trigger a rollback by default.
- The transaction will be rolled back. (Correct answer)
- The transaction will be committed because no propagation level was specified.
- The behavior is undefined and requires explicit configuration of `rollbackFor`.
Correct answer: The transaction will be rolled back.
By default, Spring's declarative transaction management is configured to automatically trigger a rollback for any unchecked exceptions (`RuntimeException` and its subclasses) and `Error`s. It does not, by default, roll back for checked exceptions.
Question 4: A financial reporting service needs to run a long-running transaction that reads multiple rows, performs complex calculations, and then reads the same rows again to verify data consistency. It is critical that the data from the initial read does not change during the transaction. Which transaction isolation level should be used to prevent this "non-repeatable read" phenomenon?
- READ_COMMITTED
- READ_UNCOMMITTED
- REPEATABLE_READ (Correct answer)
- SERIALIZABLE
Correct answer: REPEATABLE_READ
`REPEATABLE_READ` is the isolation level that guarantees if a row is read multiple times within the same transaction, the result will always be the same. It prevents both dirty reads and non-repeatable reads. `READ_COMMITTED` (a common default) only prevents dirty reads, allowing other transactions to modify and commit changes that would be visible on a subsequent read within the original transaction. `SERIALIZABLE` also prevents non-repeatable reads but is stricter, also preventing phantom reads at a higher concurrency cost.
Question 5: A developer has a Spring `@Service` bean with two public methods. The first method, `processData()`, is not transactional. It calls the second method within the same class, `saveData()`, which is annotated with `@Transactional`. When an external bean calls `processData()`, what happens to the transaction for the `saveData()` call?
- A new transaction is started correctly for the `saveData()` method.
- An `IllegalStateException` is thrown because a non-transactional method is calling a transactional one.
- The entire `processData()` method becomes transactional automatically.
- The call to `saveData()` is executed, but no transaction is created. (Correct answer)
Correct answer: The call to `saveData()` is executed, but no transaction is created.
Spring's declarative transaction management is implemented using AOP proxies. When an external bean calls a method, it goes through the proxy which applies the transactional advice. However, a method call within the same object (a self-invocation) bypasses the proxy and calls the method directly. Because the call to `saveData()` does not go through the proxy, the `@Transactional` annotation is not processed, and no transaction is started for that method call.
Question 6: Which of the following scenarios is the most compelling reason to use programmatic transaction management with `TransactionTemplate` instead of the declarative `@Transactional` annotation?
- When a method needs to operate on multiple data sources.
- When you need to implement transaction logic with finer-grained control, such as starting and committing multiple distinct transactions within a single business method. (Correct answer)
- When managing transactions on private methods within a bean.
- When developing applications that must be portable across different transaction managers.
Correct answer: When you need to implement transaction logic with finer-grained control, such as starting and committing multiple distinct transactions within a single business method.
The primary advantage of programmatic transaction management is its fine-grained control. While `@Transactional` is excellent for defining transaction boundaries at the method level, `TransactionTemplate` (or a direct `PlatformTransactionManager`) allows a developer to begin, commit, and roll back transactions at specific points *within* a method's logic. This is useful for complex workflows that don't map cleanly to a single method's scope, such as conditional commits or multiple separate transactions in one operation.
An `OrderService` method, annotated with `@Transactional`, calls a public method in an `InventoryService` bean which is annotated with `@Transactional(propagation = Propagation.REQUIRES_NEW)`.
If the `InventoryService` method completes successfully, but an unhandled exception is thrown later within the `OrderService` method, what will be the final state of the database changes?