Spring @Transactional注解:自我调用

24

我知道当在同一个类中调用事务方法时,它将不会在事务中运行。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);
}

假设saveAB被另一个对象调用,saveB 发生异常,所以saveA成功完成但saveB未能。据我所知, 即使saveAsaveB不是事务性的(因为它们是从同一对象调用的),由于saveAB是事务性的,应该仍然回滚。

我不明白的是为什么人们说自我调用会破坏事务?只要调用方法是事务性的,一切都应该按预期工作吧?这里有什么我没有理解的东西吗?


1
你的外部方法已经被注释,所以没有问题。但是如果外部方法没有被注释,那么就会有问题,因为既不会对 saveA 也不会对 saveB 进行事务处理。在这种情况下,自我调用是一个问题。 - M. Deinum
2个回答

26
我不明白的是为什么有人说自我调用会破坏事务?
我从未听说自我调用会破坏事务。我知道的只是自我调用不会启动新的事务,你已经提到了原因。
Spring的事务管理规范摘录:“注意,在代理模式下(默认情况下),只有通过代理进入的外部方法调用才会被拦截。这意味着自我调用实际上是目标对象中的一个方法调用另一个目标对象方法,即使被调用的方法标有@Transactional,在运行时也不会导致实际的事务。”
如果您从saveAB()注释掉@Transactional注解,您会发现即使saveA()和saveB()标记了@Transactional,它们也不会在事务下运行。但是,如果您从类外部调用saveA()或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);
}

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


5
如果您调用saveAB并且saveB抛出异常,事务将回滚。
自我调用不会破坏事务上下文,因为默认的事务传播是REQUIRED,这意味着在调用新的@Transactional方法时,相同的事务上下文将被重用。
然而,在同一个bean内部,调用新方法不会经过TransactionalInterceptor,因此将重用完全相同的事务上下文。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接