Transaction Management in Spring Boot

Learn via video courses
Topics Covered

Overview

What are Database Transactions?

To understand how transactions work, it's helpful to think of them as instructions that must be executed in a specific order. For example, imagine that you are transferring money from one bank account to another. In this case, the transaction might include instructions to:

  • Decrement the balance of the source account
  • Increment the balance of the destination account

These instructions must be executed correctly for the account balance to remain accurate post-transfer activity. For that to happen, either each action should be completed or none of them. This is where database transaction helps in maintaining the correct state.

Introduction

A database transaction is a unit of work performed against a database. It typically includes one or more SQL statements that read and/or update the data in the database. The key characteristic of a transaction is that it is atomic, meaning that it is either completed in its entirety or not completed at all. This ensures that the data in the database remains consistent, even if a failure or error occurs.

ACID Properties of a Transaction

In the context of a transaction, the acronym refers to four key properties of a transaction Atomicity, Consistency, Isolation, and Durability.

acid-properties-of-a-transaction

Atomicity:

Atomicity refers to the idea that database transactions (i.e., a sequence of operations that must be treated as a unit) are either completed in their entirety or not at all. This means that if any part of a transaction fails, the entire transaction is rolled back to its previous state, ensuring that the database remains consistent.

For example, a transaction that moves money from one account to another should do both operations in one unit of work to ensure data consistency.

Consistency:

Consistency refers to the property that ensures that any data written to the database must follow all of the rules and constraints set for the database. This means that data must be valid and accurately reflect the real-world relationships between entities.

A transaction takes the database from one consistent state to another.

acid-properties-of-a-transaction-2

Isolation:

Isolation refers to the property that ensures that concurrent transactions do not interfere with each other. In other words, each transaction is executed as if it is the only one happening, even if multiple transactions are happening simultaneously. This is important because it ensures that the results of one transaction do not affect the results of another, which can lead to inconsistencies in the database.

RDBMS typically has four types of isolation levels. Typically isolation levels from lowest to highest are Read Uncommitted, Read Committed, Repeatable Read, and Serializable.

Read committed isolation level is default and works for most use cases. The choice of isolation level depends on the specific use case and should be chosen carefully.

Performance is inversely proportional to isolation level.

acid-properties-of-a-transaction-3

In summary, the database isolation level determines the level of protection from the effects of concurrent transactions. Different isolation levels provide different trade-offs between concurrency and consistency, and the appropriate level should be chosen based on the application's specific needs.

Durability:

Durability is the property that ensures that once a transaction has been committed (i.e., completed and saved), it will not be lost even if the system fails. This is typically achieved through logs and backups, which allow the database to be restored to a known good state in the event of a failure.

In summary, the ACID properties are essential for maintaining the integrity and reliability of a database. They ensure that transactions are atomic, consistent, isolated, and durable, critical requirements for any DBMS.

Don't miss the chance to become a Spring Boot expert. Enroll in our Free Spring Boot Course and gain the skills to create modern and efficient Java projects.

How to Perform Transaction Management in Spring Boot?

Transaction management in spring boot is super easy using the @Transaction annotation.

Two ways we can perform a transaction in spring boot are:

  1. Programmatic
  2. Declarative

Both work, but declarative transactions are simple to work with.

Declarative Transaction

Declarative transactions involve separating the transactional logic from the business logic of your application. You do not have to write explicit code to handle transactions. Instead, you can use annotations or XML configuration to declare which methods should be executed within a transaction.

To create a declarative transaction in Spring Boot, you can use the @Transactional annotation. This annotation can be applied to a method or a class to indicate that the method or all methods in the class should be executed within a transaction. Here is an example:

In this example, the updateUserData() method is annotated with @Transactional, which means that the data update operation will be executed within a transaction. If any exceptions are thrown during the execution of this method, the transaction will be rolled back, and the data will not be saved to the database.

Overall, declarative transactions make it easier to write transactional code in your Spring Boot application and help to ensure that your data is consistent and properly managed.

Programmatic Transaction

Alternatively, you can also use the TransactionTemplate class provided by Spring to create a programmatic transaction. Here is an example:

In this example, the TransactionTemplate is used to execute the data update operation within a transaction. If any exceptions are thrown during the execution of this method, the transaction will be rolled back, and the data will not be saved to the database.

Spring Transaction Abstractions

Spring Framework's declarative transaction support is enabled via AOP proxies. Whenever the caller invokes a method or class with @Transactional annotation, it first invokes a proxy that uses a TransactionInterceptor in conjunction with an appropriate PlatformTransactionManager implementation to drive transactions around method invocations.

Conceptually, calling a method on a transactional proxy looks like this.

spring-transaction-abstractions

Properties in @Transactional Annotation

The following attributes are supported in the @Transactional annotation.

  • Propagation: Set transaction propagation from the enum Propagation.
  • Isolation: Set isolation level from the enum Isolation.
  • Timeout: Transaction timeout in seconds
  • RollbackFor: An optional array of names of exception classes that must cause a rollback.
  • NoRollBackFor: an optional array of exception classes that must not cause a rollback.
  • readOnly: Read or read/write transaction.

What are @Transactional Propagation Levels Used for?

Transaction propagation determines how a new transaction is started when a method marked with the @Transactional annotation is called from within another @Transactional method. In Spring, transaction propagation is defined using the propagation attribute of the @Transactionalannotation.

There are several different propagation levels that you can use, each of which determines how the new transaction is started:

  • PROPAGATION_REQUIRED:
    This is the default propagation level. If a transaction already exists when the method is called, that transaction will be used. Otherwise, a new transaction will be started.

    transactional-propagation-levels

  • PROPAGATION_REQUIRES_NEW:
    This propagation level always starts a new transaction, regardless of whether a transaction already exists. The existing transaction, if any, will be suspended until the new transaction is completed.

    transactional-propagation-levels-2

  • PROPAGATION_SUPPORTS:
    This propagation level will use an existing transaction if one exists, but it will not start a new transaction if none exists.

  • PROPAGATION_NOT_SUPPORTED:
    This propagation level will never start a new transaction and will run the method without a transaction. If a transaction already exists, it will be suspended until the method has been completed.

  • PROPAGATION_NEVER:
    This propagation level will never start a new transaction and throw an exception if a transaction already exists. You can specify the propagation level you want to use by setting the propagation attribute of the @Transactional annotation when you declare your transactional method. For example:

    This will cause the doSomething() method to be executed within a new transaction, regardless of whether a transaction already exists when the method is called.

What are @Transactional Isolation Levels Used for?

Transaction isolation determines the level of isolation a transaction has from other transactions running concurrently. In other words, it determines how much a transaction can see and be affected by changes made by other concurrent transactions. In Spring Boot, transaction isolation is defined using the isolation attribute of the @Transactional annotation.

There are several different isolation levels that you can use, each of which provides a different level of isolation:

  • ISOLATION_DEFAULT:
    This is the default isolation level and it uses the default isolation level of the underlying database. This is typically equivalent to ISOLATION_READ_COMMITTED.

  • ISOLATION_READ_UNCOMMITTED:
    This isolation level allows a transaction to read data written by other transactions that have not yet been committed. This can lead to dirty reads, where a transaction reads data later rolled back by another transaction.

  • ISOLATION_READ_COMMITTED:
    This isolation level ensures that a transaction can only read data that other transactions have committed. This prevents dirty reads but does not prevent non-repeatable reads or phantom reads.

  • ISOLATION_REPEATABLE_READ:
    This isolation level ensures that a transaction will always read the same data, even if other transactions are modifying that data concurrently. This prevents dirty reads and non-repeatable reads but does not prevent phantom reads.

  • ISOLATION_SERIALIZABLE:
    This isolation level provides the highest isolation level, ensuring that transactions are executed in a serializable order. This prevents dirty reads, non-repeatable reads, and phantom reads.

    When you declare your transactional method, you can specify the isolation level you want to use by setting the isolation attribute of the @Transactional annotation. For example:

Readonly vs Read-Write Transaction

The readOnly attribute of the @Transactional annotation in Spring can indicate that a transaction should be read-only, meaning that it will not attempt to modify the database in any way. This can be useful for optimizing the performance of your application because read-only transactions can often be executed more quickly than transactions that modify the database.

To specify that a transaction should be read-only, you can set the readOnly attribute of the @Transactional annotation to true when you declare your transactional method.

For example:

Conclusion

In this article, we have covered the following:

  • Transaction management is a critical aspect of any enterprise application, as it ensures the integrity and consistency of data by allowing groups of related operations to be executed as a single unit (a "transaction").
  • Transaction management in Spring Boot using the @Transaction annotation allows you to specify the behavior of your transactions using a declarative approach.
  • Spring boot declarative transactions can be applied using annotations or XML configuration rather than having to write explicit code to handle transactions in your application.
  • @Transaction annotation accepts various customization options using annotation attributes like propagation, isolation, etc.
  • Overall, Spring Boot's support for transaction management makes it easy to ensure the consistency and integrity of your application's data and helps to ensure that your transactions are properly managed and executed. Whether you are building a simple web application or a complex enterprise system, Spring Boot's transaction management features can help you to write robust and maintainable code.