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.
Так что вы ничего не пропустили в своем примере.