Spring Transaction Management

Mert Alptekin
Sahibinden Technology
4 min readDec 7, 2023

Introduction:

Transactions allow us to execute multiple database operations ensuring that all the operations are either succeed or fail together. In software development managing transactions in our code can be complex. Spring framework can be used as a helpful tool when we need to deal with transactional operations in our code. It provides an abstraction for different transaction APIs and also provides both declarative and programmatic transaction management. This way, developers can focus more on business logic code rather than transaction management code.

In this article, we will look at types of transactions and and how Spring should be configured for our needs. We will also discuss transaction propagation, isolation levels, exception handling and how to configure them with Spring. We’ll solely focus on declarative transaction management although Spring also supports programmatic way.

Transaction Types:

There are two types of transactions that we can use in our application code : global transactions, local transactions. Global transactions involve multiple resources (multiple databases, message queues) whereas local transactions are resource-specific, meaning a single database or resource. Spring provides support for both of them. In order to Spring manage the transactions, you need to configure your datasources in your xml or in configuration beans.

Spring’s PlatformTransactionManager interface can be used to define the transaction strategy. You can use it for JDBC, JTA, Hibernate etc.

For JDBC, DataSourceTransactionManager must be configured and the relaed datasource bean should be referenced

If you use a JAVA EE container you can make use of JtaTransactionManager.

For Hibernate, you need to define a HibernateTransactionManager bean and LocalSessionFactoryBean bean for the transaction manager.

Declarative Transaction Management:

Spring supports both programmatic and declarative transaction management. Declarative way is more widely used as it’s more readable and maintainable.

@Transactional annotation: By adding this annotation to your class or methods, you tell Spring the execute your code within a transactional context. Spring uses AOP concept to make it work. Under the hood, Spring weaves proxy classes for classes annotated with @Transactional. That way, it intercepts the calls to methods and it can create, commit or rollback the transaction. This annotation can only be applied to public methods. (AspectJ can used for non-public methods). Also only external method calls are intercepted meaning that if you call another transactional method in the same class, it won’t exhibit transactional behavior at runtime because of the proxy mode. A way to solve this would be wire the bean itself and call the defined bean.

@Transactional annotations can have various attributes to configure the behavior.

Propagation Behavior:

Transaction propagation defines how transactions behave with existing transactions. It’s set with propagation attribute of @Transaction annotation. i.e. @Transactional(propagation = Propagation.REQUIRED). There are several propagation behaviors in Spring:

  • Propagation.REQUIRED: If there is no transaction, Spring creates one. If the method is called within an already created transaction, it participates the existing transaction.
  • Propagation.REQUIRES_NEW: Spring creates a new transaction, if there’s an existing transaction, it became suspended.
  • Propagation.NESTED: The method creates a nested transaction within the existing transaction. If there is none, it creates a new transaction.
  • Propagation.SUPPORTS: If there’s a transaction the method participates it. If not, it’s executed non-transactionally.
  • Propagation.MANDATORY: The method must run within an existing transaction, otherwise, it throws an exception
  • Propagation.NOT_SUPPORTED: The method executes non-transactionally, suspending the existing transaction if there is one.
  • Propagation.NEVER: The method runs non-transactionally, there’s a transaction it throws exception.

Isolation Levels:

Transaction isolation is useful for controlling visibility of changes made by one transaction to other concurrent transactions. With Spring we can modify isolation level of the transaction according to our application’s requirements. Isolation level attribute can be set using @Transactional annotation i.e. @Transactional(isolation = Isolation.READ_UNCOMMITTED) If not set, the DEFAULT isolation level would be used, which is the default isolation level of the underlying datastore.

  • Isolation.READ_UNCOMMITTED:

This isolation level allows dirty reads, meaning it can read uncommitted changes made by other transactions. It may result inconsistent data. You should use it when immediate access to changes is needed.

  • Isolation.READ_COMMITTED:

Uncommitted changes made by other transactions cannot be read. It avoids dirty reads but it allows non-repeatable read, a value read within the same transaction might differ if another transaction modifies the data.

  • Isolation.REPEATABLE_READ:

This isolation level prevents dirty reads and non-repeatable reads. However it allows phantom reads, which are new rows inserted by other transactions.

  • Isolation.SERIALIZABLE:

It prevents dirty reads, non-repeatable reads, and phantom reads. However, it can lead to slower performance due to increased locking. This isolation level can be used if data consistency is crucial and performance can be sacrificed.

Exception Handling and Rollbacks:

To handle exceptions and rollbacks, Spring offer several strategies. If an exception is thrown in the transactional method, Spring automatically rolls back the transaction.

You can also customize the rollback behavior with rollbackFor attribute for spring to rollback in case of a specific exception.

@Transactional(rollbackFor = SomeException.class)

You can also prevent rollback in case of a specific exception with noRollbackFor attribute.

@Transactional(noRollbackFor = SomeException.class)

Conclusion:

Managing transactions is crucial for ensuring data integrity, reliability, and consistency. Spring Framework provides a powerful and flexible transaction management solutions that helps us develop more readable and maintainable code with declarative way. The behavior of the transaction context such as propagation, isolation level and and exception handling can be easily defined with attributes of @Transactional attribute.

References:

https://docs.spring.io/spring-framework/reference/data-access/transaction.html

--

--