Spring provides support for both programmatic and declarative transactions similar to EJB.
Programmatic Transactions – With programmatic transactions , transaction management code like, commit when everything is successful or rolling back if anything goes wrong is clubbed with the business logic.
Declarative Transactions-Declarative transactions separates transaction management code from business logic. Spring supports declarative transactions using transaction advice (using AOP).
Choosing declarative or programmatic transactions is convenience versus fine control. Programmatic approach provides a fine control on transaction boundaries, whereas declarative approach provides a great configurability using configuration files.
If our application is working with one data source only then we can manage the transactions using commit and rollback methods of transaction object, but to have a transaction between multiple data sources we would need to rely on transaction management provides by J2EE servers . Spring also supports distributed (XA) transactions which can be used for later case.
Declarative transaction management in Spring
By using Declarative transaction for Spring transaction management you keep transaction management separate from the business code. You can define declarative transactions using annotations or XML based configuration using AOP.
The annotation used for Declarative transaction management is @Transactional annotation. You can place the @Transactional annotation before an interface definition, a method on an interface, a class definition, or a public method on a class.
To make Spring framework aware of the @Transactional annotation you will have to define <tx:annotation-driven/> element in your XML configuration.
<tx:annotation-driven transaction-manager="txManager"/>
If you using Java configuration then you can enable @Transactional annotation support by adding @EnableTransactionManagement to your config class.
@Configuration @EnableTransactionManagement public class AppConfig{ ... ... }
@Transactional settings in Spring framework
You can provide transaction properties like propagation behavior, isolation level along with @Transactional annotation. Full list of the properties of the @Transactional annotation are as follows-
- propagation– Optional propagation setting.
- isolation– Optional isolation level. Only applicable to propagation REQUIRED or REQUIRES_NEW.
- timeout– Optional transaction timeout. Only applicable to propagation REQUIRED or REQUIRES_NEW. Defined in seconds using an int value.
- readOnly– Read/write vs. read-only transaction. Only applicable to REQUIRED or REQUIRES_NEW.
- rollbackFor- Optional array of exception classes that must cause rollback.
- rollbackForClassName– Optional array of names of exception classes that must cause rollback.
- noRollbackFor– Optional array of exception classes that must not cause rollback.
- noRollbackForClassName– Optional array of names of exception classes that must not cause rollback.
The default @Transactional settings are as follows:
- Propagation setting is PROPAGATION_REQUIRED.
- Isolation level is ISOLATION_DEFAULT.
- Transaction is read/write.
- Transaction timeout defaults to the default timeout of the underlying transaction system, or to none if timeouts are not supported.
- Any RuntimeException triggers rollback, and any checked Exception does not.
Example using @Transactional annotation
@Transactional(readOnly = true, propagation=Propagation.SUPPORTS) public class TestService implements FooService { public Foo getValue(String Id) { // do something } // these settings have precedence for this method @Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW, timeout=60, rollbackFor=ValueNotFoundException.class) public void updateValue(Person person) { // do something } }
Here at the class level TestService class is annotated with @Transactional annotation which is for all the methods in the class that all the methods will support transaction and will be read only. Method updateValue overrides it by having its own @Transactional annotation which requires a new transaction, read only is false and transaction is rolled back if ValueNotFoundException is thrown.
Transaction propagation Behavior
Transaction Propagation behavior can be specified in element. Below are some of the most commonly used propagation modes.
- REQUIRED– Current method must run in an existing transaction and if there are no existing transactions the start a new transaction and run within it.
- REQUIRES NEW – Current method must start a new transaction and run within it.
- SUPPORTS – Current method can run in existing transaction if exists else it is not necessary to run within a transaction.
- NOT SUPPORTED – The current method should not run within a transaction.
- MANDATORY – The current method must run within a transaction. If there’s no existing transaction in progress, an exception will be thrown.
Isolation Levels:Degree to which particular transaction is isolated from other Transaction.
When multiple transactions in the application are operating concurrently on the same data can lead to below problems.
- Dirty Read – If there are two transactions running concurrently and one thread has read the data which is being updated but not yet committed by another transaction and instead of committing, it rolls back the changes.
- Non-repeatable read– This problem occurs when a transaction gets the different data for the same query when executed multiple times. This usually happens if another transaction is committing the data simultaneously.
- Phantom Read– this problem occurs when a transaction is inserting new data while another transaction is reading the data. In this scenario reading transaction will find additional data which was not there earlier.In ideal scenario, transactions should be completely isolated from each other.
Following are the most commonly used Isolation levels and are supported by Spring.
- DEFAULT – This isolation level uses the default isolation level of the underlying database.
- READ UNCOMMITTED – This isolation level supports transactions to read uncommitted data by other transactions as well. With this isolation, there are chances of dirty read, nonrepeatable read, and phantom read.
- READ COMMITTED – This isolation level supports transactions to read only data committed by other transactions as well. There are chances of nonrepeatable read and phantom read.
Choosing Transaction Manager
Instead of managing the transaction, Spring supports several transaction managers which delegate the transaction management responsibilities to platform specific implementations. Plarform Transaction manager is the parent of all transaction manager implementations.
Some of the transaction managers are-
- DataSource Transaction manager – We can use DataSourceTransactionManager for simple JDBC persistence mechanism. Sample configuration of DataSourceTransactionManager looks like below
-
<bean id="txManager" class= "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
- Hibernate Transaction manager – Hibernate transaction manager should be used when our application is using Hibernate. Sample configuration of HibernateTransactionManager looks like below
<bean id="txManager" class= "org.springframework.orm.hibernate5.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean>
- JPA Transaction manager –Use below configuration to use JPA transaction manager .
<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <propertyname="entityManagerFactory" ref="entityManagerFactory"/></bean>
- Jta Transaction manager – If our transaction is across multiple data sources than we need to use Java Transactions API transactions . Internally JTA implementation handles transaction responsibility.
Use below configuration to configure JTA transaction manager.
<bean id="txManager" class= "org.springframework.transaction.jta.JtaTransactionManager" />
Programmatic Transaction Management
Spring Framework provides two means of programmatic transaction management-
- Using TransactionTemplate.
- Using a PlatformTransactionManager implementation directly.
Mostly declarative transaction that too using annotation is used so we’ll focus on that. If you want to use TransactionTemplate then you can configure it as follows by providing the reference of the Transaction manager.
<propertyname="transactionTemplate"> <bean class="org.springframework.transaction.support. TransactionTemplate"> <property name="transactionManager" ref="txManager" /> </bean> </property>
Spring provides platform independent Transaction manager API with the help of an abstract transaction management unit known as PlatformTransactionManager interface under org. springframework.transaction package.
This interface defines below methods
- getTransaction(TransactionDefinition)– This method returns a transaction (either a active transaction or create a new one)
- void commit(TransactionStatus)– This method commits the transaction based on the status.
- void rollback(TransactionStatus)– This method rollbacks the transaction based on status.
TransactionStatus interface defines several methods to get the status of transaction and controls the transaction execution as well.
Method and Description |
flush: Flush the underlying session to the datastore, if applicable: for example, all affected Hibernate/JPA sessions. |
hasSavepoint: Return whether this transaction internally carries a savepoint, that is, has been created as nested transaction based on a savepoint. |
isCompleted: Return whether this transaction is completed, that is, whether it has already been committed or rolled back. |
isNewTransaction: Return whether the present transaction is new; otherwise participating in an existing transaction, or potentially not running in an actual transaction in the first place. |
isRollbackOnly: Return whether the transaction has been marked as rollback-only (either by the application or by the transaction infrastructure). |
setRollbackOnly: Set the transaction rollback-only. |