Generic Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Overview

This tutorial will discuss the right way to configure Spring Transactions, how to use the @Transactional annotation and common pitfalls.

For a more in-depth discussion on the core persistence configuration, check out the Spring with JPA tutorial.

Basically, there are two distinct ways to configure Transactions – annotations and AOP – each with their own advantages. We're going to discuss the more common annotation-config here.

Further reading:

Configuring Separate Spring DataSource for Tests

A quick, practical tutorial on how to configure a separate data source for testing in a Spring application.

Quick Guide on Loading Initial Data with Spring Boot

A quick and practical example of using data.sql and schema.sql files in Spring Boot.

Show Hibernate/JPA SQL Statements from Spring Boot

Learn how you can configure logging of the generated SQL statements in your Spring Boot application.

2. Configure Transactions

Spring 3.1 introduces the @EnableTransactionManagement annotation that we can use in a @Configuration class and enable transactional support:

@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 we're using a Spring Boot project, and have a spring-data-* or spring-tx dependencies on the classpath, then transaction management will be enabled by default.

3. Configure Transactions With XML

Before 3.1 or if Java is not an option, here is the XML configuration, using annotation-driven and the namespace support:

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

4. The @Transactional Annotation

With transactions configured, we can now annotation a bean with @Transactional either at the class or method level:

@Service
@Transactional
public class FooService {
    //...
}

The annotation supports further configuration as well:

  • the Propagation Type of the transaction
  • the Isolation Level of the transaction
  • a Timeout for the operation wrapped by the transaction
  • a readOnly flag – a hint for the persistence provider that the transaction should be read only
  • the Rollback rules for the transaction

Note that – by default, rollback happens for runtime, unchecked exceptions only. The checked exception does not trigger a rollback of the transaction. We can, of course, configure this behavior with the rollbackFor and noRollbackFor annotation parameters.

5. Potential Pitfalls

5.1. Transactions and Proxies

At a high level, Spring creates proxies for all the classes annotated with @Transactional – either on the class or on any of the methods. The proxy allows the framework to inject transactional logic before and after the running method – mainly for starting and committing the transaction.

What's important to keep in mind is that, if the transactional bean is implementing an interface, by default the proxy will be a Java Dynamic Proxy. This means that only external method calls that come in through the proxy will be intercepted. Any self-invocation calls will not start any transaction, even if the method has the @Transactional annotation.

Another caveat of using proxies is that only public methods should be annotated with @Transactional. Methods of any other visibilities will simply ignore the annotation silently as these are not proxied.

This article discusses further proxying pitfalls in great detail here.

5.2. Changing the Isolation Level

We can also change the transaction isolation level:

@Transactional(isolation = Isolation.SERIALIZABLE)

Note that this has actually been introduced in Spring 4.1; if we run the above example before Spring 4.1, it will result in:

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

5.3. 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 we can't be sure that an insert or update will not occur when the readOnly flag is set. This behavior is vendor dependent, whereas JPA is vendor agnostic.

It's 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 call 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.

5.4. Transaction Logging

A helpful method to understand transactional related issues is fine-tuning logging in the transactional packages. The relevant package in Spring is “org.springframework.transaction”, which should be configured with a logging level of TRACE.

6. Conclusion

We covered the basic configuration of transactional semantics using both Java and XML, how to use @Transactional and best practices of a Transactional Strategy.

As always, the code presented in this article is available over on Github.

Generic bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE
18 Comments
Oldest
Newest
Inline Feedbacks
View all comments
Stephane
Stephane
6 years ago

Hi Eugen,

Thanks for the article. too bad I don’t have Tweeter nor G+ 🙂 Waiting for the LinkedIn share button…

By the way, I do use the @Transactional annotation but never used the @EnableTransactionManagement and it seems to work just fine. My two cents here…

Kind Regards,

Stephane Eybert

Eugen Paraschiv
6 years ago
Reply to  Stephane

Hey Stephane – yeah, the Linkedin button is probably not going to be here to soon :). There are many ways ot enable transaction management – in XML for example or even registering the necessary low level beans by hand, so you don’t have to necessarily use @EnableTransactionManagement. It’s just a quick and useful shortcut. Cheers,
Eugen.

Ridha
Ridha
4 years ago

Hi Eugen, can you just explain what do you mean by : “registering the necessary low level beans by hand”
how to do so?

Regards
Ridha

Eugen Paraschiv
4 years ago
Reply to  Ridha

Hey Ridha, So, the way the enable-style Spring annotations work (much like any other high level configuration) is – when the context gets bootstrapped, based on these annotations, Spring will create the necessary beans based on the high level config. It does that so that you can simply enable transactions and you don’t have to manually set up the transaction support beans manually (which would be a lot of work). However, if you do need to go with the manual route (which is very rare) – you can of course do that and not use the higher level annotation (or… Read more »

Ridha
Ridha
4 years ago

yep, Many thanks 🙂

karthik
karthik
6 years ago

Can i apply @Transactional annotation to a private method like

@Transaction

private Long assetDetails(AssetDTO assetDto){
….

vehicle = vehicleRepository.save(assetDto.getVehicleRepository);
vehicleLoan =assetDto.getVehicleLoan();
vehicleLoan.setVehicle(vehicle.getId);
vehicleLoanRepository.save(vehicleLoan);

}

Is it a right way of doing ? Before i work with hibernate There i used Transaction rollback() and commit();
Here rollback() functionality not working

Eugen Paraschiv
6 years ago
Reply to  karthik

Private methods will not be proxied – so no, that won’t work. The rollback semantics don’t work because the method isn’t transactional. Hope it helps. Cheers,
Eugen.

Murali
Murali
6 years ago

Hi Eugen,

I have recently implemented this in my project and I had some observation which I would like to share with you, to see if you have overcome such a scenario.

If you have appContext.xml and dispatcher-servlet.xml – having this transaction (xml) configuration in say dispatcher-servlet.xml registers the @Transactional with MVC context only. Whereas if I have a service which is not invoked through MVC controller then the @Transactional wouldn’t work or gets intercepted.

To overcome this I have to include the transaction manager and annotation driven entries in appContext.xml as well.

Does this sound familiar to you?

Eugen Paraschiv
6 years ago
Reply to  Murali

Hi Murali – that’s a common problem in Spring web apps – having something live in a different context than where you need it. You need to make sure that you have your transaction configuration live in the same context you’re trying to use it in. If you want to use the annotation at the service level – make sure that the context in which your services are bootstrapped is the same that actually enables the transactional semantics in Spring.
Hope that helps. Cheers,
Eugen.

Thomas
Thomas
5 years ago

Wow this saved me. All my attempts to debug this failed. All tutorials, and documentation that I’ve read were not able to point me in the right direction. Once I added the transaction to my servlet xml file it worked. no more null transaction exception.
Thanks :disqus and @Murali

Eugen Paraschiv
5 years ago
Reply to  Thomas

Hey Thomas – I’m glad the comment section proved helpful. Cheers,
Eugen.

Artur
Artur
5 years ago
Reply to  Murali

Ohh Man, this post realy saved my life after few hours of looking for the answer why I’m getting “No transactional entityManager exception”. Great knowledge, hard to find this answer even in stactoverflow. Many thanks and good luck

Eugen Paraschiv
5 years ago
Reply to  Artur

Glad the article helped. Cheers,
Eugen.

Bill
Bill
5 years ago

Eugen:

Great article as usual, however, you missed one of your bullet points:

detailed Rollback configuration

This is the real point of the transaction, rollback on failure. So when does this happen, what is the default behavior and how can we control that? What are the gotchas?

Bill

Eugen Paraschiv
5 years ago
Reply to  Bill

I added a clarification to the article. Now – the best practices with rollback are not a black and white kind of decision. The play into your particular architecture and transactional strategy you’re using – which is something I plan to write about in future articles. Cheers,
Eugen.

Wellington Moreira
Wellington Moreira
5 years ago
Reply to  Bill

Bill, it would be valuable for you to read database ACID:
Atomicity – All tasks in a transaction are executed, or none of them.
Consistency – Database will remain with a consistent state – if transaction is finished or not.
Isolation – No concurrency on data change, avoiding errors and inconsistent state
Durability – If a transaction was successfully finished, task is done and data was changed accordingly to your needs.

Hope it could be helpful.
Wellington

rswrc
rswrc
4 years ago

Why this tutorial does not cover Spring TrnsactionTemplate: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/support/TransactionTemplate.html ? Is this not needed?

Eugen Paraschiv
4 years ago
Reply to  rswrc

For the most part – no, it’s not needed. This article covers the declarative way of managing transactions, not the programatic way – which is why that template isn’t really necessary. Cheers,
Eugen.

Comments are closed on this article!