春季@Transactional注释:自调用

我知道当一个事务方法从同一类内部调用时,它不会在事务中运行。Spring为事务方法创建一个代理,并将它们包装在一个try-catch块中,并在发生异常时回滚。请考虑以下情形:

@Transactional
public void saveAB(A a, B b)
{
    saveA(a);
    saveB(b);
}

@Transactional
public void saveA(A a)
{
    dao.saveA(a);
}

@Transactional
public void saveB(B b)
{
    dao.saveB(b);
}

假设是从另一个对象调用的,并且 中发生了异常,因此成功完成但没有完成。据我所知,即使并且不是事务性的(因为它们是从同一个对象调用的),由于是事务性的,它仍然应该回滚。saveABsaveBsaveAsaveBsaveAsaveBsaveAB

我不明白的是,为什么人们说自我调用会破坏交易?只要调用方方法是事务性的,难道一切都不应该按预期工作吗?我在这里错过了什么吗?


答案 1

我不明白的是,为什么人们说自我调用会破坏交易?

我从未听说过自我调用会破坏交易。我所知道的是,自我调用不会启动新的交易,你已经提到了原因。

来自Spring's Transaction Management Specification的片段

注意在代理模式(这是默认设置)中,仅拦截通过代理传入的外部方法调用。这意味着,实际上,即使调用的方法标有@Transactional,自调用(实际上,目标对象内的方法调用目标对象的另一个方法)也不会在运行时导致实际的事务。


如果从 中删除注释,则会观察到该方法,并且不会在事务下运行,即使它用 .但是,如果调用或从类外部调用,它将按预期在事务下运行。这就是为什么人们建议谨慎对待自我调用的原因。@TransactionsaveAB()saveA()saveB()@TransactionalsaveA()saveB()

public void saveAB(A a, B b)
{
    saveA(a);
    saveB(b);
}

@Transactional
public void saveA(A a)
{
    dao.saveA(a);
}

@Transactional
public void saveB(B b)
{
    dao.saveB(b);
}

在我看来,自我调用任何公共方法都是一个坏主意。


答案 2

如果您调用并抛出 一个 ,您的事务将回滚。saveABsaveBException

自调用不会破坏事务上下文,因为默认的事务传播是 ,这意味着在新 Bean 上调用新方法时会重用相同的事务上下文。REQUIRED@Transactional

但是,在同一个 Bean 中,调用新方法不会经过 ,因此会重用完全相同的事务上下文。TransactionalInterceptor