The Transactional annotation in Spring
Spring is a popular framework for developing Java ♨️ applications that provides a lot of features to make development easier and more efficient. One of these features is transaction management, which is crucial for ensuring data consistency and integrity in a database-driven application. Spring provides a declarative transaction management model that allows developers to specify transactional behavior using annotations, including the @Transactional
annotation.
In this post, we will explore what the @Transactional
annotation is, how it works, and how it can be used to manage transactions in Spring.
Since this blog post won’t go into a lot of details, if you want a lower level view of what does the Transactional annotation do under the hood (like how it uses the JDBC Transaction Management) this is an unbelievable article to read and it’s well-worth your time, IMO: https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth
What is a Transactional Annotation in Spring?
A transactional annotation is a way to define transactional behaviour for a method or class in Spring. The @Transactional
annotation allows developers to specify the transactional boundaries for a method or class, which Spring will use to manage transactions for that method or class.
When a method or class is marked with the @Transactional
annotation, Spring will automatically create a transaction before the method is executed and commit the transaction after the method has completed. If an exception is thrown during the execution of the method, Spring will automatically roll back the transaction to ensure data consistency.
The @Transactional
annotation can be applied to both public methods and public classes. When applied to a class, all public methods of that class will be executed within a transactional context unless overridden by a method-specific @Transactional
annotation.
Transactional Behavior Options
The @Transactional
annotation supports various options that can be used to configure the transactional behavior. Some of the most commonly used options include:
propagation
: Specifies the propagation behavior for the transaction. This option determines how the transaction should behave when a transactional method is called within the scope of an existing transaction.isolation
: Specifies the isolation level for the transaction. This option determines how the transaction should behave in a concurrent environment.timeout
: Specifies the timeout duration for the transaction. This option determines how long a transaction can run before it times out.readOnly
: Specifies whether the transaction should be read-only. This option can be used to optimize performance when a transaction only reads data from the database.
Example Usage
Here is an example of how the @Transactional
annotation can be used in a Spring service class:
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
public void saveUser(User user) {
userRepository.save(user);
}
@Transactional(readOnly = true)
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class)
public void updateUser(User user) throws Exception {
userRepository.save(user);
if (user.getName().equals("invalid")) {
throw new Exception("Invalid user name");
}
}
}
In this example, the UserService
class is annotated with @Transactional
, which means that all public methods of this class will be executed within a transactional context. The saveUser
method simply saves the user to the database, while the getUserById
method retrieves a user by its ID. The updateUser
method updates a user and throws an exception if the user's name is "invalid". The @Transactional
annotation on this method specifies that the propagation behavior should be Propagation.REQUIRED
, the isolation level should be Isolation.READ_COMMITTED
, and the method should rollback if any exception of type Exception
is thrown.
It’s important to understand the different propagation behaviors
.. and choose the appropriate option based on your requirements. The wrong propagation behavior can lead to unexpected behavior and data inconsistencies in your application. Here are all propagation options, currently supported in Spring:
- REQUIRED: This is the default propagation behavior in Spring
@Transactional
. If an existing transaction already exists, the current method will use it, or it will create a new transaction otherwise. This option is suitable for most cases and ensures that all methods involved in a transactional operation are part of the same transaction. - REQUIRES_NEW: This propagation behavior creates a new transaction every time the annotated method is called, regardless of any existing transactions. If there’s an existing transaction, it’s suspended, and a new transaction is started for the annotated method. Once the annotated method completes, the suspended transaction resumes. This option is useful when you want to ensure that a specific operation is always executed in a new transaction.
- MANDATORY: This propagation behavior specifies that the annotated method must be called from within a transactional context. If there’s no existing transaction when the method is called, Spring will throw an exception. This option is useful when you have a method that requires a transactional context to operate correctly.
- NEVER: This propagation behavior specifies that the annotated method must not be called within a transactional context. If there’s an existing transaction when the method is called, Spring will throw an exception. This option is useful when you have a method that should never be part of a transactional operation.
- SUPPORTS: This propagation behavior specifies that the annotated method can be called with or without an existing transaction. If there’s an existing transaction when the method is called, it will participate in that transaction. If there’s no transaction, the method will execute outside of a transactional context. This option is useful when you have a method that can be used within or outside of a transactional context.
- NOT_SUPPORTED: This propagation behavior specifies that the annotated method should not participate in a transactional context. If there’s an existing transaction when the method is called, it’s suspended until the method completes. This option is useful when you have a method that should not be part of a transactional operation.
- NESTED: This propagation behavior specifies that the annotated method should be executed within a nested transaction that’s part of the existing transaction. The nested transaction can be rolled back independently of the parent transaction. If there’s no existing transaction when the method is called, Spring will create a new transaction. This option is useful when you need to create a savepoint within a transaction to allow for partial rollbacks.
We’re not going to dive deep into the different isolation levels and features the Transactional provides, because they are quite complex and out of scope for this post. You can see more info about the isolation levels here.
Conclusion
The @Transactional
annotation is a powerful feature in Spring that makes transaction management much easier and more efficient. By using this annotation, developers can specify transactional boundaries for their methods and classes, and let Spring handle the rest. This allows developers to focus on their business logic and leave the transaction management to Spring, which is more efficient and less error-prone.
In conclusion, the @Transactional
annotation is an essential tool for managing transactions in Spring applications. By using this annotation, developers can easily specify the transactional behavior for their methods and classes and let Spring handle the transaction management. The @Transactional
annotation is configurable with various options, making it a flexible tool that can be used to manage transactions in a variety of scenarios.
If you’re new to Spring or transaction management, it’s recommended that you learn more about the @Transactional
annotation and practice using it in your Spring applications. Spring provides excellent documentation on this topic, and there are also many online resources and tutorials available to help you get started. With practice and experience, you'll be able to use the @Transactional
annotation to manage transactions in your Spring applications with confidence and ease. 💪