UnexpectedRollbackException переопределяет мое собственное исключение - PullRequest
5 голосов
/ 14 июля 2010

У меня есть следующий странный сценарий с управлением транзакциями Spring:

У меня есть метод A, который вызывает метод B, который вызывает метод C, каждый из которых относится к своему классу.Методы B и C заключены в транзакции.Оба используют PROPAGATION_REQUIRED, поэтому пока Spring создает две логические транзакции, в БД есть одна физическая транзакция.

Теперь в методе CI выдается исключение RuntimeException.Это устанавливает внутреннюю логическую транзакцию как rollbackOnly, а также физическую транзакцию.В методе B я знаю о возможности UnexpectedRollbackException, поэтому я не продолжаю фиксировать в обычном режиме.Я ловлю исключение из C и выкидываю еще одно RuntimeException.

Я ожидаю, что внешнее RuntimeException вызовет откат к внешней транзакции, однако фактическое поведение таково:

  • внешняя транзакция пытается зафиксировать или хотя бы проверить ее состояние, а затем выдает исключение UnexpectedRollbackException, поскольку физическая транзакция уже была помечена как rollbackOnly.
  • Прежде чем вызвать это исключение, оно печатает в журналах другое исключение,заявив, что «исключение приложения переопределено исключением из коммита».Таким образом, вызывающий объект A получает исключение UnexpectedRollbackException, а не исключение, которое выбрасывает B.

Я нашел для него обходной путь, который заключается в активной установке внешней транзакции в качестве отката только перед созданием исключения

public ModelAndView methodB(HttpServletRequest req, HttpServletResponse resp) {
  try{
    other.methodC();
  } catch (RuntimeException e){
    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    throw new RuntimeException ("outer exception");
  }
  return handleGetRequest(req, resp);
}

Однако этот обходной путь сильно связывает код с транзакциями api, и я бы хотел этого избежать.Любые предложения?

ps обе транзакции предназначены для отката на исключения во время выполнения.Я не определил откат для исключения или что-то в этом роде

Ответы [ 2 ]

6 голосов
/ 24 июля 2010

Я нашел причину этой проблемы. Оказывается, что methodB был обернут прокси на основе cglib (используя Spring Old Way, до 2.0), прежде чем обернут в транзакции. поэтому, когда я выбрасываю RuntimeException из methodB, cglib заканчивает тем, что выбрасывает InvocationTargetException, который на самом деле является проверенным исключением.

Диспетчер транзакций Spring заканчивает тем, что ловит проверенное исключение и пытается зафиксировать транзакцию, не зная о вложенном исключении времени выполнения, которое выкинул methodB. Как только я обнаружил это, я настроил оболочку транзакции на откат и для проверенных исключений, теперь она работает как положено.

5 голосов
/ 14 июля 2010

Вы можете попытаться установить флаг failEarlyOnGlobalRollbackOnly на true в своем наследнике AbstractPlatformTransactionManager (например, HibernateTransactionManager).Вот его описание:

Устанавливает, происходит ли сбой на ранней стадии в случае, если транзакция глобально помечена как только для отката.

По умолчанию установлено значение "false",только вызывая UnexpectedRollbackException на самой внешней границе транзакции.Включите этот флаг, чтобы вызвать исключение UnexpectedRollbackException, как только глобальный маркер только для отката был обнаружен, даже изнутри внутренней границы транзакции.Отметим, что начиная с Spring 2.0 поведение при сбое на раннем этапе для глобальных маркеров «только откат» было унифицировано: все диспетчеры транзакций по умолчанию вызывают исключение UnexpectedRollbackException только на самой внешней границе транзакции.Это позволяет, например, продолжить модульные тесты даже после сбоя операции, и транзакция никогда не будет завершена.Все менеджеры транзакций будут терпеть неудачу раньше, только если этот флаг был явно установлен в «true».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...