We start with a standard summary of a transaction description. However, keep in mind that all we are going to write about applies to more than just database operations. In most cases, it is true that a database is heavily involved, but nothing stops us from extending the concept of transactions from the database to other transactional resources.
Transactions have the four notoriously known ACID properties—atomicity, consistency, isolation, and durability—and it is up to the transactional resources to maintain these aspects of a transaction. You cannot control the atomicity, consistency, and durability of a transaction, but you can control the transaction propagation and timeout, which set whether the transaction should be read-only and specify the isolation level.
Spring encapsulates all these settings in a TransactionDefinition interface. This interface is used in the core interface of the transaction support in Spring, the PlatfromTransactionManager, whose implementations perform transaction management on a specific platform, such as JDBC or JTA. The core method, PlatformTransactionManager.getTransaction(), returns a TransactionStatus interface, which is used to control the transaction execution, more specifically to set the transaction result and to check whether the transaction is read-only or whether it is a new transaction.
As we mentioned earlier, the TransactionDefinition interface controls the properties of a transaction. Let's take a more detailed look at TransactionDefinition interface (see Listing 12-1) and describe its methods.
Listing 12-1: TransactionDefinition Interface
package org.springframework.transaction; import java.sql.Connection; public interface TransactionDefinition { int getPropagationBehavior(); int getIsolationLevel(); int getTimeout(); boolean isReadOnly(); }
The simple and obvious methods of this interface are getTimeout(), which returns the time (in seconds) in which the transaction must complete, and isReadOnly(), which indicates whether the transaction is read-only. The transaction manager implementation can use this value to optimize the execution and to check to make sure that the transaction is performing only reads.
The other two methods, getPropagationBehavior() and getIsolationLevel(), need to be discussed in more detail. We begin with getIsolationLevel(), which controls what changes to the data other transactions see. Table 12-1 lists the transaction isolation levels you can use and explains what changes made in the current transaction other transactions can access.
Isolation Level | Description |
---|---|
TransactionDefinition.ISOLATION_DEFAULT | Default isolation level for individual PlatformTransactionManager. |
TransactionDefinition.ISOLATION_READ_UNCOMMITTED | Lowest level of isolation; it is barely a transaction at all because it allows this transaction to see data modified by other uncommitted transactions. |
TransactionDefinition.ISOLATION_READ_COMMITTED | Default level in some databases; it ensures that other transactions are not able to read data that has not been committed by other transactions. Unfortunately, you can select data inserted or updated by other transactions. |
TransactionDefinition.ISOLATION_REPEATABLE_READ | More strict than ISOLATION_READ_COMMITED; it ensures that once you select data, you can select at least the same set again. This implies that if another transaction inserts new data, you can select the newly inserted data. |
TransactionDefinition.ISOLATION_SERIALIZABLE | The most expensive and reliable isolation level; all transactions are treated as if they were executed one after another. |
Choosing the appropriate isolation level is very important for the consistency of the data, but making these choices can have a great impact on performance. The highest isolation level, TransactionDefinition.ISOLATION_SERIALIZABLE, is particularly expensive to maintain.
The getPropagationBehavior() method specifies what happens to a transactional call depending on whether there is an active transaction. The values for this method are listed in Table 12-2.
Propagation Behavior | Description |
---|---|
TransactionDefinition.PROPAGATION_REQUIRED | Supports a transaction if one already exists. If there is no transaction, it starts a new one. |
TransactionDefinition.PROPAGATION_SUPPORTS | Supports a transaction if one already exists. If there is no transaction, it executes non-transactionally. |
TransactionDefinition.PROPAGATION_MANDATORY | Supports a transaction if one already exists. Throws an exception if there is no active transaction. |
TransactionDefinition.PROPAGATION_REQUIRES_NEW | Always starts a new transaction. If an active transaction already exists, it is suspended. |
TransactionDefinition.PROPAGATION_NOT_SUPPORTED | Does not support execution with an active transaction. Always executes non-transactionally and suspends any existing transaction. |
TransactionDefinition.PROPAGATION_NEVER | Always executes non-transactionally even if an active transaction exists. Throws an exception if an active transaction exists. |
TransactionDefinition.PROPAGATION_NESTED | Runs in a nested transaction if an active transaction exists. If there is no active transaction, the execution is executed as if TransactionDefinition.PROPAGATION_REQUIRED is set. |
This interface, shown in Listing 12-2, allows a transactional manager to control the transaction execution. The code can check whether the transaction is a new one, or whether it is a read- only transaction and it can initiate a rollback.
Listing 12-2: TransactionStatus Declaration
public interface TransactionStatus { boolean isNewTransaction(); void setRollbackOnly(); boolean isRollbackOnly(); }
The methods of the TransactionStatus interface are fairly self-explanatory, the most notable one is setRollbackOnly(), which causes a rollback and ends the active transaction.
This is an interface that uses the TransactionDefinition and TransactionStatus interfaces to create and manage transactions. The actual implementations of this interface must have detailed knowledge of the transaction manager. The DataSourceTransactionManager controls transactions performed within a DataSource; HibernateTransactionManager controls transactions performed on a Hibernate session, JdoTransactionManager manages JDO transactions, and JtaTransactionManager delegates transaction management to JTA.