Transaction configuration with JPA and Spring 3.1

FROM HERE

This is the fifth of a series of articles about Persistence with Spring. This article will focus on the configuration of transactions with Spring 3.1 and JPA. For a step by step introduction about setting up the Spring context using Java based configuration and the basic Maven pom for the project, see this article.

The Persistence with Spring series:

Spring and Transactions

Spring provides a consistent and comprehensive transaction abstraction over many supported environments. There are two distinct ways of configuring and using transactions – annotations and AOP – each with their own advantages. The reference should provide enough material on both the decision to use the Spring transaction programming model, as well as a in depthdiscussion of its architecture.

Configuring transactions with annotations

After the introduction of Java based configuration in Spring 3.0, configuring transactions was one of the last things to require XML. Spring 3.1 introduces the new @Enable annotations – @EnableTransactionManagement – which makes it possible to fully use Java for the configuration:

@Configuration
@EnableTransactionManagement
public class PersistenceJPAConfig{
   
   @Bean
   public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
      ...
   }
   
   @Bean
   public PlatformTransactionManager transactionManager(){
      JpaTransactionManager transactionManager = new JpaTransactionManager();
      transactionManager.setEntityManagerFactory(
       entityManagerFactoryBean().getObject() );
      return transactionManager;
   }
   
}

However, if Java is not an option, here is the XML equivalent of this configuration:

<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
   <property name="entityManagerFactory" ref="myEmf" />
</bean>
<tx:annotation-driven transaction-manager="txManager" />

For a full discussion and code samples on configuring Spring with JPA, check out a previous article of this series.

The @Transactional configuration

By default, @Transactional will set the propagation to REQUIRED, the readOnly flag to false, and the rollback only for unchecked exceptions. Also note that the isolation level is set to the database default; when using JPA, the isolation level is that of the underlying persistence provider. In the case of Hibernate, the isolation level of all transactions should beREPEATABLE_READ.

For the purpose of this discussion, the relevant application layers will be DAO, Service and Controller. These layers can of course vary from application to application, without changing the underlying principles discussed here.

The @Transactional semantics of the Service and DAO layers should both be configured with REQUIRED propagation and thereadOnly flag set to true for the relevant methods. The Controller layer should contain no transaction logic.

Note that this is also the way the DAO implementation is configured in Spring Data. For a detailed analysis of the persistence layer with Spring data, see the previous article of this series.

Assuming a clean separation of layers, where the Controller layer will only invoke the Service layer, which in turn will only call the DAO, then the DAO layer will never be called in a non-transactional context. As such the DAO will never be the transaction owner, and a more strict transaction configuration should be used for it. In this situation, the transactional semantics should beMANDATORY propagation and no readOnly flag. The MANDATORY propagation will simply ensure that a transaction has already been started when the DAO layer is entered, double checking the stated assumption that the DAO is never the transaction owner. The readOnly flag is also not needed because it will be set by the transaction owner as well.

The API layer transaction strategy

With the previous transaction configuration, the Service layer is the transaction owner – it is responsible with starting the transaction and it contains any potential rollback logic. This does however have one downside: a Service write method can invoke another Service write method; because both may contain rollback logic, the transaction owner may not have full control over the rollback logic in some circumstances. For this reason care must be taken if the transaction owner resubmits the transaction or takes corrective action – in these cases the service logic may need to be refactored to avoid this scenario.

The API layer strategy is meant to address this shortcoming by making the Controller layer the transaction owner and working with the assumption that a public Controller method shouldn’t invoke another in any scenario. With this transaction strategy, the DAO and Service transactional semantics remain the same as before; the Controller layer however will use theREQUIRES_NEW propagation to ensure that it invokes the underlying Service layer in a transactional context.

01.@Transactional( propagation = Propagation.REQUIRES_NEW )
02.public class Controller{
03....
04.}
05.@Transactional( propagation = Propagation.MANDATORY )
06.public class Service{
07....
08.}
09.@Transactional( propagation = Propagation.MANDATORY )
10.public class DAO{
11....
12.}

Spring Testing support for Transactions

The TestContext framework provides transaction support Spring enabled tests via the PlatformTransactionManager bean in the application context and the @Transactional annotation.

Spring also support various test specific annotations which, used in conjunction with the TestContext framework allow for full control over the transaction configuration: @TransactionConfiguration, @Rollback and @BeforeTransaction/@AfterTransaction.

A common usage of the framework and a common requirement of an integration test is to leave the database unchanged after the test method is finished. This can be done by annotating the test class with:

1.@TransactionConfiguration( defaultRollback = true )
2.@Transactional
3.public class TransactionalIntegrationTest{
4....
5.}

Also, note that because Spring uses proxies at runtime to manage transactions, the test class must not be final if it’s annotated with@Transactional.

Potential PitfallsChanging the Isolation level

One of the major pitfalls when configuring Spring to work with JPA is that changing the isolation of the transaction semantics will not work – JPA does not support custom isolation levels. This is a limitation of JPA, not Spring; nevertheless changing the@Transactional isolation property will result in:

org.springframework.transaction.InvalidIsolationLevelException: Standard JPA does not support custom isolation levels – use a special JpaDialect for your JPA implementation

Read Only Transactions

The readOnly flag usually generates confusion, especially when working with JPA; from the javadoc:

This just serves as a hint for the actual transaction subsystem; it will not necessarily cause failure of write access attempts. A transaction manager which cannot interpret the read-only hint will not throw an exception when asked for a read-only transaction.

The fact is that it cannot be guaranteed that an insert or update will not occur when the readOnly flag is set – its behavior isvendor dependent whereas JPA is vendor agnostic.

It is also important to understand that the readOnly flag is only relevant inside a transaction; if an operation occurs outside of a transactional context, the flag is simply ignored. A simple example of that would calling a method annotated with:

@Transactional( propagation = Propagation.SUPPORTS,readOnly = true )

from a non-transactional context – a transaction will not be created and the readOnly flag will be ignored.

HERE is another good article about spring transactional TEST

HERE

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s