Как перезапустить транзакции при взаимоблокировке/тайм-ауте блокировки в Spring?

Какова наилучшая практика реализации перезапуска транзакции при возникновении взаимоблокировки или тайм-аута блокировки при использовании Spring (в частности, рекомендуемый подход Spring: декларативные транзакции)?

Спасибо,

Асаф


person Community    schedule 19.11.2009    source источник


Ответы (4)


Я чувствую, что у Spring должен быть хороший ответ на этот вопрос (по крайней мере, в виде документации или какого-то перехватчика повторных попыток). Увы, это не так.

Вероятно, лучший способ справиться с повторными попытками (если вы хотите продолжать быть «декларативным» в отношении вещей) — это написать собственную реализацию перехватчика, которая будет автоматически повторять транзакцию заданное количество раз. Для начала изучите Spring TransactionInterceptor, который управляет поведением start/rollback/commit для декларативных транзакций. Если вы используете Hibernate, обратите внимание, как он обрабатывает привязку/отвязку сеанса Hibernate к текущему потоку.

На что следует обратить внимание, если вы используете Hibernate:

  • Ваш «перехватчик повторных попыток» должен обязательно отвязать любой ранее существовавший сеанс Hibernate с привязкой к потоку и повторно привязать новый. Как только из кода Hibernate/JDBC выдается исключение (например, взаимоблокировка), соответствующий сеанс Hibernate становится отравленным, и его необходимо отбросить. (session.clear() недостаточно.)
  • Будьте осторожны, если ваши методы транзакционной службы используют объекты сеанса Hibernate в качестве параметров метода. При повторной попытке при сбросе сеанса Hibernate эти объекты будут отсоединены. Вам нужно будет повторно прикрепить их, если метод службы предполагает, что они прикреплены (например, если они используют лениво загружаемые свойства, доступ к которым осуществляется в методе службы, или если вы пытаетесь их сохранить и т. д.). В общем, лучше, если вы не используйте объекты Hibernate в качестве параметров транзакционных методов обслуживания.
  • Вы будете реализовывать MethodInterceptor.invoke() -- экземпляр MethodInvocation, который передается в this, может иметь состояние; вам может потребоваться клонировать его перед использованием в перехватчике.
person Community    schedule 04.05.2010

Я рекомендую использовать класс org.springframework.retry.interceptor.RetryOperationsInterceptor из весеннего повтора проекта, сконфигурированный как это:

<aop:config>
    <aop:pointcut id="transactional" expression="execution(* com...*Service.remoteCall(..))" />
    <aop:advisor pointcut-ref="transactional" advice-ref="retryAdvice" order="-1"/>
</aop:config>

<bean id="retryAdvice" class="org.springframework.retry.interceptor.RetryOperationsInterceptor"/>

Но если вы все же хотите реализовать его самостоятельно, пример АОП из документации Spring — хорошее начало.

person Community    schedule 04.02.2013
comment
Спасибо за упоминание проекта spring-retry. - person Vineet Bhatia; 05.01.2015

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

Я бы использовал АОП в случае сценария автоматического перезапуска.

person Community    schedule 20.11.2009

У меня был тот же вопрос несколько лет назад, и в итоге я написал мое собственное решение как аспект АОП, который в конечном итоге выглядит в вашем коде следующим образом:

  @RetryTransaction
  @Transactional
  public void doSomething() {
      ....
  }
person Community    schedule 24.03.2015