Skip to content

Spring Transaction

Vishnu Garg edited this page Aug 21, 2018 · 4 revisions

Spring Transaction Management

A database transaction is a sequence of actions that are treated as a single unit of work. These actions should either complete entirely or take no effect at all. Transaction management is an important part of RDBMS-oriented enterprise application to ensure data integrity and consistency.

  • Atomicity − A transaction should be treated as a single unit of operation, which means either the entire sequence of operations is successful or unsuccessful.

  • Consistency − This represents the consistency of the referential integrity of the database, unique primary keys in tables, etc.

  • Isolation − There may be many transaction processing with the same data set at the same time. Each transaction should be isolated from others to prevent data corruption.

  • Durability − Once a transaction has completed, the results of this transaction have to be made permanent and cannot be erased from the database due to system failure.

Local Vs Global Transaction Management

1. Local transaction management : It can be useful in a centralized computing environment where application components and resources are located at a single site, and transaction management only involves a local data manager running on a single machine. Local transactions are easier to be implemented.

2. Global transaction management : It is required in a distributed computing environment where all the resources are distributed across multiple systems. In such a case, transaction management needs to be done both at local and global levels. A distributed or a global transaction is executed across multiple systems, and its execution requires coordination between the global transaction management system and all the local data managers of all the involved systems.

Programmatic vs. Declarative

Spring supports two types of transaction management.

Programmatic transaction management− This means that you have to manage the transaction with the help of programming. That gives you extreme flexibility, but it is difficult to maintain.

Declarative transaction management − This means you separate transaction management from the business code. You only use annotations or XML-based configuration to manage the transactions.

Declarative transaction management is preferable over programmatic transaction management though it is less flexible than programmatic transaction management, which allows you to control transactions through your code. But as a kind of crosscutting concern, declarative transaction management can be modularized with the AOP approach. Spring supports declarative transaction management through the Spring AOP framework.

Spring Transaction Abstractions

The key to the Spring transaction abstraction is defined by the org.springframework.transaction.PlatformTransactionManager interface, which is as follows −

PlatformTransactionManager

public interface PlatformTransactionManager {
   TransactionStatus getTransaction(TransactionDefinition definition);
   throws TransactionException;
   
   void commit(TransactionStatus status) throws TransactionException;
   void rollback(TransactionStatus status) throws TransactionException;
}
  • TransactionStatus getTransaction(TransactionDefinition definition) This method returns a currently active transaction or creates a new one, according to the specified propagation behavior.
  • void commit(TransactionStatus status) This method commits the given transaction, with regard to its status.
  • void rollback(TransactionStatus status) This method performs a rollback of the given transaction.

TransactionDefinition

The TransactionDefinition is the core interface of the transaction support in Spring and it is defined as follows −

public interface TransactionDefinition {
/*
This method returns the propagation behavior. Spring offers all of the transaction propagation options familiar from EJB CMT.
/*
   int getPropagationBehavior();

/*
This method returns the degree to which this transaction is isolated from the work of other transactions.
/*
   int getIsolationLevel();


/*
Name of Transaction
/*
   String getName();

//This method returns the time in seconds in which the transaction must complete.
   int getTimeout();

//This method returns whether the transaction is read-only.
   boolean isReadOnly();
}

Isolation level

  1. TransactionDefinition.ISOLATION_DEFAULT : This is the default isolation level.
  2. TransactionDefinition.ISOLATION_READ_UNCOMMITTED Indicates that dirty reads, non-repeatable reads, and phantom reads can occur.
  3. TransactionDefinition.ISOLATION_READ_COMMITTED Indicates that dirty reads are prevented; non-repeatable reads and phantom reads can occur.
  4. TransactionDefinition.ISOLATION_REPEATABLE_READ Indicates that dirty reads and non-repeatable reads are prevented; phantom reads can occur.
  5. TransactionDefinition.ISOLATION_SERIALIZABLE Indicates that dirty reads, non-repeatable reads, and phantom reads are prevented.

What do these dirty reads, phantom reads, or repeatable reads mean.

  • Dirty Reads: Transaction 'A' writes a record. Meanwhile Transaction 'B' reads that same record before Transaction A commits. Later Transaction A decides to rollback and now we have changes in Transaction B that are inconsistent. This is a dirty read. Transaction B was running in READ_UNCOMMITTED isolation level so it was able to read Transaction A changes before a commit occurred.
  • Non-Repeatable Reads: Transaction 'A' reads some record. Then Transaction 'B' writes that same record and commits. Later Transaction A reads that same record again and may get different values because Transaction B made changes to that record and committed. This is a non-repeatable read.
  • Phantom Reads: Transaction 'A' reads a range of records. Meanwhile Transaction 'B' inserts a new record in the same range that Transaction A initially fetched and commits. Later Transaction A reads the same range again and will also get the record that Transaction B just inserted. This is a phantom read: a transaction fetched a range of records multiple times from the database and obtained different result sets (containing phantom records).

Propagation Types

  1. TransactionDefinition.PROPAGATION_MANDATORY Supports a current transaction; throws an exception if no current transaction exists.

  2. TransactionDefinition.PROPAGATION_NESTED Executes within a nested transaction if a current transaction exists.

  3. TransactionDefinition.PROPAGATION_NEVER Does not support a current transaction; throws an exception if a current transaction exists.

  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED Does not support a current transaction; rather always execute nontransactionally.

  5. TransactionDefinition.PROPAGATION_REQUIRED Supports a current transaction; creates a new one if none exists.

  6. TransactionDefinition.PROPAGATION_REQUIRES_NEW Creates a new transaction, suspending the current transaction if one exists.

  7. TransactionDefinition.PROPAGATION_SUPPORTS Supports a current transaction; executes non-transactionally if none exists.

TransactionStatus

The TransactionStatus interface provides a simple way for transactional code to control transaction execution and query transaction status.

public interface TransactionStatus extends SavepointManager {

//This method returns true in case the present transaction is new.
   boolean isNewTransaction();

/This method returns whether this transaction internally carries a savepoint, i.e., has been created as nested transaction //based on a savepoint.
   boolean hasSavepoint();


//This method returns whether the transaction has been marked as rollback-only.
   boolean isRollbackOnly();
//This method sets the transaction as rollback-only.
   void setRollbackOnly();

//This method returns whether this transaction is completed, i.e., whether it has already been committed or rolled back.
   boolean isCompleted();
}

@Transactional: Service or DAO Layer?

  • The Service is the best place for putting @Transactional, service layer should hold the detail-level use case behavior for a user interaction that would logically go in a transaction.
  • There are a lot of CRUD applications that don't have any significant business logic for them having a service layer that just passes data through between the controllers and data access objects is not useful. In these cases we can put transaction annotation on Dao.
  • So in practice you can put them in either place, it's up to you.
  • Also if you put @Transactional in DAO layer and if your DAO layer is getting resused by different services then it will be difficult to put it on DAO layer as different services may have different requirements.
  • If your service layer is retrieving objects using Hibernate and let's say you have lazy initializations in your domain object definition then you need to have a transaction open in service layer else you will face LazyInitializationException thrown by the ORM.
  • Consider another example where your Service layer may call two different DAO methods to perform DB operations. If your first DAO operation failed then other two may be still passed and you will end up inconsistent DB state. Annotating Service layer can save you from such situations. Reference
  • Spring Transaction
Clone this wiki locally