Search code examples
javaspringtransactions

Why doesn't @Transactional roll back after NullPointerException is thrown?


I have a method which can throw a NullPointerException. The method is marked with @Transactional.

My code structure is as follows:

public void outer() {
    try {
        inner();
    } catch (Exception e) {
        // exception caught
    }     
}

@Transactional
public void inner() {
    database.saveStuff();
    throw new NullPointerException();
}

After running the above code, the database updates inside inner() are not rolled back. What could cause such problem?

As per my understanding, the database actions should be rolled back if the exception is "visible" to the annotation. In this case meaning that the inner() method is marked with @Transactional and throws the exception instead of catching it.

It might also be worth mentioning that the method outer() is also called inside another @Transactional method, which in turn is inside a try catch. However, the exception is not visible to that method because it's caught inside outer(), so as per my understanding it shouldn't matter.

I tried to search for answers on the web, but all the answers seem to be about checked Exceptions (e.g. java.lang.Exception) not automatically being rolled back. Because NullPointerException is a subclass of RuntimeException, that shouldn't be an issue here.

Somebody in my team also suggested to use this cluster of annotations, but it didn't make any difference:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor=Exception.class, isolation=Isolation.READ_COMMITTED)

Solution

  • Because you are calling inner() directly. Spring AOP only works with bean method since inner() is called directly it won't be proxied and the interceptors are not added.

    As you mentioned the outer method is called from a method with @Transactional So, you should either remove the try-catch or rethrow the exception.