Skip to main content

Spring's transaction management with the @Transactional annotation

What is a database transaction?

A database transaction is any operation that is treated as a single unit of work that either completes fully or does not complete at all and leaves the storage system in a consistent state.

Transactions can impact a single record or multiple records.

A.C.I.D. properties

ACID is an acronym that stands for atomicity, consistency, isolation, and durability.

ACID properties ensure that a database transaction (a set of read, write, update, or delete operations) leaves the database in a consistent state even in the event of unexpected errors.

1) Atomicity

Atomicity guarantees that all the commands in a transaction (to read, write, update, or delete data) are treated as a single unit and either succeed or fail together.

The transaction would have either completed successfully or been rolled back if any part of it failed.

2) Consistency

Consistency guarantees that the data is in a consistent state when a transaction starts and when it ends.

If the data gets into an illegal state, the whole transaction fails.

3) Isolation

Isolation ensures that the intermediate state of a transaction is invisible to other transactions.

As a result, transactions that run concurrently don't interfere with each other.

4) Durability

Durability guarantees that once the transaction commit and changes are written to the database, they will persist even in the case of system failures like crashes or power outages.

As a result, the transaction's modifications are never undone.

Spring Transaction Management

Spring transaction management simply means: How does Spring start, commit, or rollback JDBC transactions.

Spring provides both declarative (@Transactional) and programmatic (using a TransactionTemplate or PlatformTransactionManager) transaction management, and developers can choose based on the requirement.

Declarative transaction management is easier and more suitable in most cases, but in some cases, you want fine-grain control, and you can use programmatic transaction management.

In order to support transactions in a spring project, you need to add @EnableTransactionManagement to a @Configuration class.

However, if we're using a Spring Boot project and have spring-data-* or spring-tx dependencies on the classpath, then transaction management will be enabled by default.

The @Transactional Annotation

Any bean or public method annotated with the @Transactional annotation makes sure that the methods will be executed inside a database transaction.

The @Transactional annotation should be used in the service layer because it is this layer's responsibility to define the transaction boundaries.

The modern Spring approach to transaction management is typically as follows:

That's it! No configuration, no XML, and no code are needed.


How does the @Transactional annotation work ?

Spring creates dynamic proxies for classes that declare @Transactional on the class itself or on methods.

It does that through a method called proxy-through-subclassing with the help of the Cglib library.

It is also worth noting that the proxy itself does not handle these transactional states (open, commit, close); the proxy delegated this work to a transaction manager.

The proxy has access to a transaction manager and can ask it to open and close transactions and connections.

Spring offers a PlatformTransactionManager (extends TransactionManager) interface, which, by default, comes with a couple of handy implementations. One of them is the DataSourceTransactionManager.

Pitfalls

As Spring wraps the bean in the proxy, only calls from "outside" the bean are intercepted. That means, any self-invocation calls will not start any transaction, even if the method has the @Transactional annotation.

Moreover, only public methods should be annotated with @Transactional. Methods of any other visibility will silently ignore the annotation, as these are not proxying.

The same is true for other annotations, such as @Cacheable.

Transaction Rollback

By default, only RuntimeException and Error trigger a rollback. A checked exception does not trigger a rollback of the transaction.

The @Transactional annotation, on the other hand, supports rollbackFor or rollbackForClassName attributes for rolling back transactions, as well as noRollbackFor or noRollbackForClassName attributes for avoiding rollback.

The @Transactional annotation attributes

The @Transactional annotation provides the following attributes:

1) Propagation

The "propagation" attribute defines how the transaction boundaries propagate to other methods that will be called either directly or indirectly from within the annotated block.

There are a variety of propagation modes that can be plugged into the @Transactional method.

                                                                                               
PropagationMeaning
REQUIREDThis is the default propagation. In this case, if no active transaction is found, spring creates one. Otherwise, the method appends to the currently active transaction:
SUPPORTSIf a transaction exists, then the method uses this existing transaction. If there isn't a transaction, it is executed non-transactional.
MANDATORYIf there is an active transaction, then it will be used. If there isn't an active transaction, then Spring throws an exception.
REQUIRES_NEWSpring suspends the current transaction if it exists and then creates a new one.
NOT_SUPPORTEDIf a current transaction exists, first Spring suspends it, and then the method runs without a transaction.
NEVERSpring throws an exception if there's an active transaction.
NESTEDSpring checks if a transaction exists, and if so, it marks a save point. If method execution throws an exception, then the transaction rolls back to this save point.

2) ReadOnly

The "readOnly" attribute defines if the current transaction is read-only or read-write.

It's a good practise to set the readOnly attribute to true at the class level and override it on a per-method basis for the methods that need to write to the database.

3) RollbackFor and RollbackForClassName

The "rollbackFor" and "rollbackForClassName" attributes define one or more Throwable classes for which the current transaction will be rolled back.

By default, only RuntimeException and Error trigger a rollback. A checked exception does not trigger a rollback of the transaction.

4) NoRollbackFor and NoRollbackForClassName

The "noRollbackFor" and "noRollbackForClassName" define one or more Throwable classes for which the current transaction will not be rolled back.

5) Isolation

The "isolation" attribute describes how changes applied by concurrent transactions are visible to each other.

Source Code: GitHub

Comments

Popular posts from this blog

Deploying Spring Boot microservices on Kubernetes Cluster

This article guides you through the deployment of two Spring Boot microservices, namely "order-service" and "inventory-service," on Kubernetes using "MiniKube" . We will establish communication between them, with "order-service" making calls to an endpoint in "inventory-service." Additionally, we will configure "order-service" to be accessible from the local machine's browser . 1) Create Spring Boot microservices The Spring Boot microservices, "order-service" and "inventory-service," have been developed and can be found in this GitHub repository. If you are interested in learning more about creating Spring Boot REST microservices, please refer to this or this (Reactive) link. 2) Build Docker Images The Docker images for both "order-service" and "inventory-service" have already been generated and deployed on DockerHub, as shown below. codeburps/order-service cod...

Circuit Breaker Pattern with Resilience4J in a Spring Boot Application

Read Also: Spring Cloud Circuit Breaker + Resilience4j Resilience4j is a lightweight fault tolerance library that draws inspiration from Netflix Hystrix but is specifically crafted for functional programming. The library offers higher-order functions, known as decorators , designed to augment any functional interface, lambda expression, or method reference with features such as Circuit Breaker, Rate Limiter, Retry, or Bulkhead . These functionalities can be seamlessly integrated within a project, class, or even applied to a single method. It's possible to layer multiple decorators on any functional interface, lambda expression, or method reference, allowing for versatile and customizable fault tolerance. While numerous annotation-based implementations exist online, this article focuses solely on the reactive approach using router predicates and router functions . How Circuit Breaker Pattern works? In general, a circuit breaker functions as an automatic electrical s...

How to create a basic Spring 6 project using Maven

Below is a step-by-step guide to creating a basic Spring project using Maven. 1) Create a Maven Project Use the following Maven command to create a new Maven project. mvn archetype:generate -DgroupId=com.tb -DartifactId=spring-demo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false 2) Import in IntelliJ IDEA If you haven't already, open IntelliJ IDEA on your system. Go to "File" > "New" > "Project from Existing Sources..." . In the file dialog, navigate to the directory where your Maven project is located. Select the pom.xml file within the project directory and click "Open." 3) Update pom.xml In total, the application requires the below-mentioned dependencies: 4) Create Spring Configuration Create a Java configuration class that uses annotations to define your Spring beans and their dependencies. This class should be annotated with @Configuration . 5) Create the Main Application C...