为什么在抛出NullPointerException后@Transactional不会回滚?

huangapple go评论73阅读模式
英文:

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

问题

我有一个可能会抛出NullPointerException的方法。该方法标有@Transactional

我的代码结构如下:

public void outer() {
    try {
        inner();
    } catch (Exception e) {
        // 捕获异常
    }     
}

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

在运行上述代码后,inner() 中的数据库更新不会被回滚。可能是什么原因导致这种问题?

根据我的理解,如果异常对注解是“可见”的话,数据库操作应该会被回滚。在这种情况下,意味着inner() 方法标有@Transactional 并抛出异常,而不是捕获异常。

值得一提的是,outer() 方法也在另一个@Transactional 方法内部调用,而后者又位于try catch块内。然而,由于异常在outer() 内部被捕获,所以根据我的理解,它对该方法不可见,因此不应该有影响。

我尝试在网络上寻找答案,但所有的答案似乎都是关于已检查异常(例如java.lang.Exception)不会自动回滚的情况。因为NullPointerExceptionRuntimeException的子类,所以在这里不应该是问题。

我的团队中有人建议使用这组注解,但这并没有产生任何影响:

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

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)

答案1

得分: 2

因为你直接调用了inner()。Spring AOP 只对bean方法起作用,因为直接调用了inner(),它不会被代理,拦截器也不会被添加。

正如你提到的,outer方法是从一个带有@Transactional的方法中调用的,所以你应该要么移除try-catch,要么重新抛出异常。

英文:

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.

huangapple
  • 本文由 发表于 2020年10月1日 20:30:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/64155462.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定