Spring Transaction Management

Introduction

One of the most compelling reasons to use the Spring Framework is the comprehensive transaction support. The Spring Framework provides a consistent abstraction for transaction management that delivers the following benefits:
  • Provides a consistent programming model across different transaction APIs such as JTA, JDBC, Hibernate, JPA, and JDO.
  • Supports declarative transaction management.
  • Provides a simpler API for programmatic transaction management than a number of complex transaction APIs such as JTA.
  • Integrates very well with Spring's various data access abstractions.

Motivations

Traditionally, J2EE developers have had two choices for transaction management: global or local transactions. Global transactions are managed by the application server, using the Java Transaction API (JTA). Local transactions are resource-specific: the most common example would be a transaction associated with a JDBC connection.

Global Transactions:

Global transactions have a significant downside, in that code needs to use JTA, and JTA is a cumbersome API to use (partly due to its exception model). Furthermore, a JTA UserTransaction normally needs to be sourced from JNDI: meaning that we need to use both JNDI and JTA to use JTA. Obviously all use of global transactions limits the reusability of application code, as JTA is normally only available in an application server environment.

Local Transactions:

Local transactions may be easier to use, but have significant disadvantages: they cannot work across multiple transactional resources. For example, code that manages transactions using a JDBC connection cannot run within a global JTA transaction. Another downside is that local transactions tend to be invasive to the programming model.

Spring resolves these problems. It enables application developers to use a consistent programming model in any environment. You write your code once, and it can benefit from different transaction management strategies in different environments. The Spring Framework provides both declarative and programmatic transaction management. Declarative transaction management is preferred by most users, and is recommended in most cases.

3. Key abstractions

The key to the Spring transaction abstraction is the notion of a transaction strategy.
A transaction strategy is defined by the org.springframework.transaction.PlatformTransactionManager interface, shown below:
public interface PlatformTransactionManager {

      TransactionStatus getTransaction(TransactionDefinition definition)
      throws TransactionException;
      void commit(TransactionStatus status) throws TransactionException;

      void rollback(TransactionStatus status) throws TransactionException;
}
The getTransaction(..) method returns a TransactionStatus object, depending on a TransactionDefinition parameter. The returned TransactionStatus might represent a new or existing transaction (if there were a matching transaction in the current call stack - with the implication being that (as with J2EE transaction contexts) a TransactionStatus is associated with a thread of execution).

The TransactionDefinition interface specifies:

Isolation:
the degree of isolation this transaction has from the work of other transactions. For example, can this transaction see uncommitted writes from other transactions? Propagation: normally all code executed within a transaction scope will run in that transaction. However, there are several options specifying behavior if a transactional method is executed when a transaction context already exists: for example, simply continue running in the existing transaction (the common case); or suspending the existing transaction and creating a new transaction. Spring offers all of the transaction propagation options familiar from EJB CMT.

There are 7 types of propagation supported by Spring :

  • PROPAGATION_REQUIRED Support a current transaction; create a new one if none exists.
  • PROPAGATION_SUPPORTS Support a current transaction; execute non-transactionally if none exists.
  • PROPAGATION_MANDATORY Support a current transaction; throw an exception if no current transaction exists.
  • PROPAGATION_REQUIRES_NEW Create a new transaction, suspending the current transaction if one exists.
  • PROPAGATION_NOT_SUPPORTED Do not support a current transaction; rather always execute non-transactionally.
  • PROPAGATION_NEVER Do not support a current transaction; throw an exception if a current transaction exists.
  • PROPAGATION_NESTED Execute within a nested transaction if a current transaction exists, behave like PROPAGATION_REQUIRED else.


In most cases, you may just need to use the PROPAGATION_REQUIRED.

In addition, you have to define the method to support this transaction attributes as well. The method name is supported wild card format, a save* will match all method name start with save.

Timeout: how long this transaction may run before timing out (and automatically being rolled back by the underlying transaction infrastructure).

Read-only status: a read-only transaction does not modify any data. Read-only transactions can be a useful optimization in some cases (such as when using Hibernate).

public interface TransactionStatus {

      boolean isNewTransaction();

      void setRollbackOnly();

      boolean isRollbackOnly();

}