Spring @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);
}

Предположим, что saveAB вызывается из другого объекта, и в saveB произошло исключение, поэтому saveA завершился успешно, но saveB - нет. Насколько мне известно, хотя saveA и saveB не являются транзакционными (потому что они вызываются из одного и того же объекта), поскольку saveAB является транзакционным, он все равно должен выполнить откат.

Чего я не понимаю, так это почему люди говорят, что самопризыв нарушает транзакцию? Пока метод вызывающей стороны является транзакционным, не должно ли все работать так, как ожидалось? Есть что-то, что я здесь скучаю?

2 ответа

Решение

Чего я не понимаю, так это почему люди говорят, что самопризыв нарушает транзакцию?

Я никогда не слышал, чтобы самовывоз нарушал транзакцию. Все, что я знаю, это то, что самовывоз не запустит новую транзакцию, и вы уже упомянули причину.

Фрагмент из спецификации управления транзакциями Spring

Примечание. В режиме прокси (который используется по умолчанию) перехватываются только вызовы внешних методов, поступающие через прокси. Это означает, что самовывоз, по сути, метод в целевом объекте, вызывающий другой метод целевого объекта, не приведет к реальной транзакции во время выполнения, даже если вызванный метод помечен @Transactional.


Если вы удалите аннотацию @Transaction из saveAB(), вы заметите, что методы 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);
}

На мой взгляд, самовывоз любого публичного метода - плохая идея.

Если вы можете сохранить saveAB и saveB выдает исключение, ваша транзакция будет откатана.

Селд-вызов нарушил бы транснациональное поведение, если бы saveAB не был аннотирован @Transactional.

Так что вы ничего не пропустили в своем примере.

Другие вопросы по тегам