Transactions with Spring 4 and JPA

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.

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.

2. Configure Transactions without XML

Spring 3.1 introduces the @EnableTransactionManagement annotation to be used in on @Configuration classes 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;
   }
}

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, a bean can now be annotated 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
  • detailed Rollback configuration

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 method being invoked – mainly for starting and committing the transaction.

What is 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 is annotated with @Transactional.

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 pitfals in great detail here.

5.2. Changing 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

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 it cannot be guaranteed that an insert or update will not occur when the readOnly flag is set – its behavior is vendor 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.

5.4. Transaction Logging

Transactional related issues can also be better understood by 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. The Spring support for transactional testing as well as some common JPA pitfalls were also discussed.

The implementation of this Spring Transaction Tutorial can be downloaded as a working sample project.

This is an Eclipse based project, so it should be easy to import and run as it is.

I usually post about Persistence on Google+ - you can follow me there:

GET THE 3 EBOOKS >>
Download the 3 eBooks - Build Your App "The Right Way"
×
Build Your Web App with Spring (and quickly prototype it to 90%)

, , ,

  • Someone

    Great post, just one thing on the following …

    “By default, @Transactional will set the propagation to REQUIRED, the readOnly flag to false, and the rollback only for checked exceptions.”

    Correct me if I´m wrong, but rollback management is set to NON-checked exceptions by default.

    • http://www.baeldung.com eugen

      Thanks for pointing out the missing “un”, good catch. Yes, rollback does only happen automatically for runtime exceptions.
      I have updated the article.

    • Gp

      No rollback is on any kind of exception

      • Eugen

        Rollback is indeed done by default for unchecked exceptions.

  • Łukasz Bachman

    Nice one here, my friend. I wanted to dig into transaction management for a long time and this is great post to help me get started Thanks!

  • Pingback: Links for December 27th through December 28th — Vinny Carpenter's blog

  • Pingback: Simple JPA Application with Hibernate « Zeeshan Akhter

  • Pingback: Spring 3 ASM – Spring asm Dependency « Zeeshan Akhter

  • Pingback: Introduction to Spring’s Aspect Oriented Programming(AOP) « Zeeshan Akhter

  • Russell

    Nice one. Very useful. One important question. How to change default transaction isolation level in JPA/Hibernate environment. The “hibernate.connection.isolation” only works if you use DriverManager which is not a option in most JEE container.

    • http://www.baeldung.com/ Eugen Paraschiv

      Yeah, isolation has always been a bit of a problem with JPA – I’ve seen solutions that were doing low level hacks in the JDBC Driver but nothing that worked well unfortunately.

  • Shagar Upadhyay

    I tried the method you suggested on “5. The API layer transaction strategy” and gave my controller ‘Propagation.REQUIRES_NEW’ and my service and DAO ‘propagation = Propagation.MANDATORY’. When I checked if it worked I get an error :

    org.springframework.transaction.IllegalTransactionStateException:
    No existing transaction found for transaction marked with propagation
    ‘mandatory’

    when i try to call a doLogin() method on my controller.

    Do I have to do any additional settings to enable this transaction behavior?

    • Muhammad Haris

      Hi,
      I’m facing the same Exception. Did you find a solution for this?
      Waiting for reply

      • Reddy

        Can some body help the on this, I am also facing the same problem on flush().

        • baeldung

          The most probable reason for this is that your Controller layer isn’t transactional – likely because the controllers themselves are not proxied; one option is to use CGLIB proxies so that the class is proxied; an alternative is to have the controllers implement an interface.
          A final note is that I updated the article and simplified it to only focus on plain transactions – Transactional Strategies is an advanced topic that I will cover in a future article.
          Hope this helps,
          Cheers,
          Eugen.

          • Reddy

            Thank you very much for your reply Baeldung.

            I added annotation to my controller @Controller and with @RequestParams.
            Auto injected repository using @Autowire.

          • Suda

            Thank you, could you please share some example links of CGLIB.

          • baeldung

            There is more than one way to do this – either via XML or annotations; one way is: @EnableTransactionManagement(proxyTargetClass = true) – but I would recommend reading the Spring reference – namely the chapter about proxies and the differences between the available mechanisms.
            Hope this helps,
            Eugen.

  • Rahul

    Getting the exception:

    javax.persistence.TransactionRequiredException: no transaction is in progress
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:959) ~[hibernate-entitymanager-3.6.10.Final.jar:3.6.10.Final]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.6.0_21]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.6.0_21]

    I have used @Transactional at DAO layer but still getting this error.

    • http://www.baeldung.com/ Eugen Paraschiv

      Hi Rahul,
      Sorry for the late reply – if you’re seeing problems with the example project on github, please raise an issue over there and I’ll take a look at the problem.
      Cheers,
      Eugen.

  • http://invasionofsmallcubes.github.io/ Il Bianconiglio

    Hi! Is there a way to share a common superclass among different PersistenceJPAConfig subclasses? My use case is I have 3 different datasource, right now I have to redefine the Bean transactionManager() in each subclass so that the transaction is open. If I leave that method on the superclass flush won’t work because no transaction is active. Thank you very much.

  • Thiruppathy.R

    Very Useful. Thank you!

  • Binh Thanh Nguyen

    Thanks, nice post

  • Stephane

    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

    • http://www.baeldung.com/ Eugen Paraschiv

      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.

      • Stephane

        All right, thanks for the reply Eugen ! In fact, my app is totally XML-free, I only used JavaConfig for its configuration.

        • http://www.baeldung.com/ Eugen Paraschiv

          Sounds good. So you’re saying that – without enabling transactions specifically – they work out of the box? Is the project somewhere I can take a look (github for example)?

          • Stephane

            You have an email address I can send this to ?

          • http://www.baeldung.com/ Eugen Paraschiv

            You can send it to eugen@baeldung.com, but I was thinking that you just put it on github and I’ll close it from there. Cheers,
            Eugen.

  • karthik

    @Eugen 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

    • http://www.baeldung.com/ Eugen Paraschiv

      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.