What I learned
Today, I had the opportunity to review the concept of transactions and explore how the @Transactional
annotation operates within a Spring Boot environment.
What are transactions
A transaction is a unit of work that ensures either all of its operations are successful or none of them are. It is usually used on data sensitive operations in which the integrity of data depends on all operations being executed successfully. For instance, one can think of the transfer of money as a group of operations that need to be arranged as a unit of transaction. The money transfer process usually involves a series of operations, including:
Withdraw from one account
Transfer of money to another account
Depositing money to the other account
In the mentioned scenario, for the transfer of money to take place, all three operations must successfully execute.
In Spring Boot, developers can utilize the @Transactional
annotation to specify a function or functions to run as a transaction. Spring Boot employs aspect-oriented programming, a programming paradigm, to seamlessly implement transactions without requiring significant intervention from the developer.
What is Aspect Oriented Programming (AOP)
AOP is a programming approach that helps to modularize a codebase. It is a pattern that groups different functionalities of code into “aspects” and reuses it whenever it is needed. This improves reusability of code and helps maintain a leaner codebase. For example logging is usually a function that a log of methods reuse. By following or using AOP principles, a codebase can define a function to run before and after certain functions are called in order to log the execution time of said functions.
Spring Start Here succinctly summarized some concepts utilized in AOP:
Terminologies used in AOP (reference: Spring documentation)
Aspect: what code is being injected
Advice: when is the code being injected.
Pointcut: which methods are targeted for injection
Join point: the trigger for aspects to run. In Spring it is always method invocation
Target object: the object that contains the join point
Having a simple understanding of AOP will help in further understanding how @Transactional
is used in Spring.
@Transactional
reference: Spring documentation
The @Transactional
annotation utilizes AOP to run a function or functions as a transaction. In the case of transactions:
Aspect: starting and ending a transaction
Advice: around the pointcut or target method, meaning before and after the pointcut is run
Pointcut: the method that is to be invoked (transaction unit)
Join point: method call
Target object: usually a service component in a Spring Boot app
To illustrate a typical flow using the @Transactional
annotation, let's refer to a diagram from the Spring documentation:
In code, an aspect using the @Transactional
annotation will appear as follows:
val manager = TransactionManager.getInstance()
fun aspect(){
try{
manager.begin()
target.invoke()
manager.commit()
}catch(e : Exception){
manager.rollback()
}
}
Parameter (readonly = true)
As a Spring Boot developer, you've likely encountered numerous methods annotated with the @Transactional(readOnly = true)
annotation. While exploring transaction concepts, I found myself intrigued by the advantages of the readOnly
parameter and the reasons behind annotating a method specifically for read-only queries.
Benefits
Performance
Applying
@Transactional(readOnly=true)
provides several optimizations:No Flushing in Hibernate: No need to synchronize data between the persistence context and DBMS.
No Dirty Checking: No need to detect changes in entity states (data changes).
DBMS Optimizations: Potential optimizations, such as avoiding locking or reading from a read replica instead of a write-read instance (varies by DBMS).
Clear Intent
Other developers can easily understand that the operation is a read-only transaction. Additionally, the annotation prevents Create, Update, Delete (CUD) operations and discourages future additions of such operations.
Drawbacks
Locking
Transactions occupy a database connection, and in the case of large transactions or operational logic within the annotated method, the connection might hinder other attempts to interact with the database.
💡Why not omit thereadonly
parameter for readonly operations?After reading various resources the aforementioned performance and clear intent benefits were main reasons for using the annotation. Moreover, another performance benefit was caching. When reading from the same entity multiple times, the persistence context acts as a first-level cache, reducing the need for direct reads from the database.