Why Doesn’t My Spring @Transactional Annotation Work?

By Ting Lin – Software Engineer – Cobalt Digital Advertising Platform

With Spring framework, wrapping a call in a transaction is easy. Adding annotation @Transactional can do the work. But sometimes, @Transactional does not work for mysterious reasons. It seems like the annotation is ignored. What causes that malfunction?

Recently, I came across this issue of @Transactional again and there no exception or error at all. What was going on? After playing with the problem for a while, I found out the root cause and a solution. I think it is worthwhile to share this with others who might not have encountered this situation before.

To demonstrate, I’ve created a sample application that inserts a record into MySQL database. The source code can be found in Resources section. To setup MySQL database, follow these instructions then run create-table.sql.

First, I just wanted to persist data in an old-fashioned way:

final EntityManagerFactory emf =
        Persistence.createEntityManagerFactory("example");
final EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();

Running this, I was confident that the O/R layer is working and hibernate-configuration was correct.

Next I wanted to do the same thing as prior, I just wanted to do it utilizing proper DAO using Spring-JPA to manage transactions.

So lets start with a simple piece of code.

@Component
public class ExampleDaoImpl {
  @PersistenceContext private EntityManager entityManager;

  @Transactional(propagation = Propagation.REQUIRED)
  public final void persist(final Example entity) {
    entityManager.persist(entity);
  }
}

And of course the test that goes with it.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration (locations = {"classpath:context.xml"})
public class ExampleDaoTest {
  @Autowired private ExampleDaoImpl dao;

  @Test
  public void shouldPersist() {
    final Example entity = new Example("example 2");
    dao.persist(entity);
  }
}

Confident in my small sample, I went ahead and ran the test… works right? Sadly, I got an exception.

Apparently, entityManager in ExampleDaoImpl was null, but how come? The console logging for Spring didn’t show any exceptions! Now I became curious as to why such a simple piece of code can be tricky…

First things first, let’s see if Spring successfully initialized ExampleDaoImpl, a simple trick is to just print something out, so I added the following constructor:


public ExampleDaoImpl() {
  System.out.println("ExampleDaoImpl hashcode = [" + System.identityHashCode(this) + "]");
}

Running the test code again, I was even more surprised, not only did the constructor print out the hashcode as expected, it did it twice!!

The constructor was called twice with different hash codes.  The seemingly simple task is becoming messier with every step.  Not to derail the task at hand, we’ll set aside the fact that it is getting called twice and just focus on why EntityManager didn’t get wired in.  If entityManager in ExampleDaoImpl was null at the time it was called, how about we pass in the entityManager?

Lets try changing ExampleDaoImpl.persist() to be:

@Transactional(propagation = Propagation.REQUIRED)
public final void persist(final EntityManager em, final Example entity) {
  em.persist(entity);
}

Now, the test code is changed to:

@Test
public void shouldPersist() {
  final Example entity = new Example("example 2");
  final EntityManagerFactory emf =
          Persistence.createEntityManagerFactory("example");
  final EntityManager em = emf.createEntityManager();
  dao.persist(em, entity);
}

SUCCESS!!  I can even see the hibernate SQL generated on the standard out inserting the record.

Now lets take a look in the database, “0 rows selected”!!  Where is the record I just inserted into database? Why was my record not committed? And how come there were no exceptions or errors thrown?

At this point, I am guessing that either the persist call somehow failed, or the transaction never ran.

Let’s take a look at the call stack that invokes ExampleDaoImpl.persist(), and BINGO!!

What I expected to see isn’t there:
org.springframework.transaction.interceptor.TransactionInterceptor.invoke

Now I’m sure the persist call was not in a transaction but why?

Not sure how to proceed from here, I decided to see if perhaps the double initialization might hold some clue, remember the two instances being created printing out two separate hashcodes.

Lets see who’s creating those, and do to that, I need to examine the call stack for each constructor, so lets print it out too.

public ExampleDaoImpl() {
  System.out.println("create instance with hashCode [" + hashCode() + "]");
  new RuntimeException().printStrackTrace();
}

Interestingly, I see two different call stacks, one involving CGLIB, and the other not.

So I do some Google searching and read this article Proxying mechanisms. In short, if a Spring bean does not implement an interface, CGLIB will be used as the proxy; and for each proxied instance, two objects are created: the actual proxied object and an instance of the subclass that implements the advice.

Okay, well that solves that riddle, but why isn’t the transaction intercepting the call?

Lets try a quick test and change the code around to use interfaces instead, see if the JDK proxy can shed any light…

We’ll change ExampleDaoImpl to implement ExampleDao

And the test to autowire the Interface…


public class ExampleDaoTest {
  @Autowired private ExampleDao dao;
  ...
}

That works great…  Now it seems that something is different about CGLIB, carefully examining the console output again, I found an interesting warning log:

WARNING: Unable to proxy method [public final void com.example.dao.ExampleDaoImpl.persist(com.example.entity.Example)] because it is final: All calls to this method via a proxy will be routed directly to the proxy.

This warning means that CGLIB will not be able to implement the aspect around the method (aka Transaction) due to the method being final.  So if we want to know why ‘final’ can cause problems for us, we need to understand how proxies are implemented. In fact, both JDK dynamic proxies and CGLIB can work. Going back to the older code, see:

CGLIB:

@Component
public class ExampleDaoImpl {
  @PersistenceContext private EntityManager entityManager;

  @Transactional(propagation = Propagation.REQUIRED)
  public void persist(final Example entity) {
    entityManager.persist(entity);
  }
}

notice method persist() is no longer final?

JDK dynamic proxies:

public interface ExampleDao {
  void persist(T entity);
}

@Component
public class ExampleDaoImpl implements ExampleDao {
  @PersistenceContext private EntityManager entityManager;

  @Override
  @Transactional(propagation = Propagation.REQUIRED)
  public final void persist(final Example entity) {
    entityManager.persist(entity);
  }
}

Great! We now know how to work with CGLIB and JDK dynamic proxies. But why @transactional doesn’t work sometimes?

CGLIB proxy extends ExampleDaoImpl and the proxy holds on to a plain instance of ExampleDaoImpl. When AOP is required, the logic of the AOP is performed in the subclass and the actual method call is then dispatched to the plain instance in the proxy.  If the method is final, the subclass can’t surround the call to it, so the actual execution is given to the parent class of the proxy subclass without any interception by the subclass..

Now remember how we got here?  We were getting NullPointerException on this call

@Transactional(propagation = Propagation.REQUIRED)
public final void persist(final Example entity) {
  entityManager.persist(entity);
}

The reason for that lies in the fact that since the persist method is final, calls to it are handled inside the proxy through inheritence and never dispatched to the plain instance contained within the proxy, so the entityManager was never auto-wired by spring.

Doing more research and further testing I summorize the findings in this table:

Component doesn’t implement Interface then use CGLIB
Method Needs AOP? Method Level Modifier Result
yes final AOP fails at runtime with WARNING: Unable to proxy method
yes not final work as proxy
no final work without proxy
no not final work without proxy
Component implements an Interface then use JDK Dynamic Proxy
Method Needs AOP? Component Used As Interface? Result
yes yes work as proxy
yes no Runtime Error, Spring will fail to autowire.
no yes work without proxy
no no Runtime Error, Spring will fail to autowire.

As you can see, JDK proxy works better than CGLIB as it doesn’t suffer an error in setup that keeps the system working only to find out later that something is wrong.

Armed with all this knowledge, I realized that months down the line, I will forget the exact values in each cell in the table I built, so to make sure I don’t fall into this same trap again in the future, I implemented a test which makes sure every component that requires AOP will implement at least one interface. In other words, we will not use CGLIB.

public class ComponentInterfaceEnforcerTest {
  // The package to test
  private static final String POJO_PACKAGE = "com.example";

  private List<PojoClass> pojoClasses;
  private PojoValidator pojoValidator;

  @Before
  public void setup() {

    pojoClasses = PojoClassFactory.getPojoClassesRecursively(
      POJO_PACKAGE, new ComponentFilter());

    pojoValidator = new PojoValidator();

    // Create Rules to validate structure for POJO_PACKAGE
    pojoValidator.addRule(new MustImplementInterface());
  }

  @Test
  public void evaluateEveryComponent() {
    for (PojoClass pojoClass : pojoClasses) {
      pojoValidator.runValidation(pojoClass);
      }
  }
}

public class ComponentFilter implements PojoClassFilter {
  private final List<Class<? extends Annotation>> beanTypes =
    Lists.newArrayList();
  private final List<Class<?>> methodAnnotations = Lists.newArrayList();

  public ComponentFilter() {
    beanTypes.add(Component.class);
    beanTypes.add(Repository.class);
    beanTypes.add(Service.class);

    methodAnnotations.add(Transactional.class);
  }

  @Override
  public boolean include(final PojoClass clazz) {
    for (final Annotation annotation : clazz.getAnnotations()) {
      if (isTargetClass(clazz, annotation.annotationType())) {
        return true;
      }
    }
    return false;
  }

  private boolean isTargetClass(final PojoClass clazz,
      final Class<? extends Annotation> classAnnotation) {
    for (final Class<? extends Annotation> beanType : beanTypes) {
      if (beanType == classAnnotation && hasAOP(clazz)) {
        return true;
      }
    }
    return false;
  }

  private boolean hasAOP(final PojoClass clazz) {
    final List<PojoMethod> methods = clazz.getPojoMethods();
    for (final PojoMethod method : methods) {
      final List<? extends Annotation> annotations = method.getAnnotations();
      for (final Annotation annotation : annotations) {
        if (isTargetMethod(clazz, annotation.annotationType())) {
          return true;
        }
      }
    }
    return false;
  }

  private boolean isTargetMethod(final PojoClass clazz,
      final Class<? extends Annotation> methodAnnotation) {
    for (final Class<?> annotation : methodAnnotations) {
      if (annotation == methodAnnotation) {
        return true;
      }
    }
    return false;
  }
}

public class MustImplementInterface implements Rule {
  @Override
  public void evaluate(final PojoClass clazz) {
    assertTrue("component " + clazz + " should implement at least one interface",
      clazz.getInterfaces().size() > 0);
  }
}

openpojo is a nice utility to make testing easier. The test uses openpojo to scan every component the application requests; then it uses reflection to make sure every method in that component, who requires @Transactional, should have an interface. With the test, we will only get a test failure at compile-time instead of runtime every time we forget to implement an interface. In other words, I will never be trapped into CGLIB problem.

From this investigation, I found that it is easy to accidentally fall into situations where Spring is not able to proxy methods in a bean.  As a rule, it is simple to adopt a policy where you always create interfaces for Spring-managed beans, and interact with beans only through their interface.  Additionally, we can create unit tests which will seek out cases where this policy is violated, so if we forget to follow this rule in the future we will be notified as soon as the tests are ran.

Resources:
Example application source: example.zip
Proxying mechanisms: http://static.springsource.org/spring/docs/3.0.0.M3/spring-framework-reference/html/ch08s06.html
Transaction management: http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Openpojo: http://code.google.com/p/openpojo/
ASM: http://asm.ow2.org/doc/tutorial.html#resources
Mysql: http://dev.mysql.com/doc/refman/5.1/en/installing.html

About collectivegenius
Everyone has a voice and great ideas come from anyone. At Cobalt, we call it the collective genius. When technical depth and passion meets market opportunity, the collective genius is bringing it’s best to the table and our customers win.

One Response to Why Doesn’t My Spring @Transactional Annotation Work?

  1. sagar says:

    thanks a lot for sharing your experience.

    Like

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

%d bloggers like this: