Как преодолеть исключение StaleObjectStateException в Grails Service

Я ввел TransactionService, который я использую в своих контроллерах для выполнения оптимистичных транзакций. Должно

  • попытаться выполнить данную транзакцию (= закрытие)
  • откатить его обратно, если он не работает и
  • попробуйте еще раз, если не получится

В основном это выглядит так:

class TransactionService {
  transactional = false // Because withTransaction is used below anyway
  def executeOptimisticTransaction(Closure transaction) {
    def success = false
    while (!success) {
      anyDomainClass.withTransaction { status ->
        try {
          transaction()
          success = true
        } catch(Exception e) {
          status.setRollbackOnly()
        }
      }
    }
  }
}

Это немного сложнее, например, он использует разные Thread.sleeps перед повторной попыткой и прерывает работу на каком-то этапе, но здесь это не имеет значения. Это вызвано от контроллеров, которые передают транзакцию, чтобы быть безопасно выполненным как закрытие.

Моя проблема: когда служба обнаруживает исключение org.hibernate.StaleObjectStateException из-за одновременных обновлений, она продолжает пытаться снова, но исключение никогда не исчезает.

Я уже пробовал разные вещи, такие как повторное присоединение классов домена в транзакции, передаваемой контроллером, очистка сеанса в службе или в контроллере, но это не помогло. Что мне не хватает?

Я должен отметить, что я получил ошибку, что мой "Диспетчер транзакций не разрешает вложенные транзакции", когда я пытался вставить savePoint до вызова транзакции () с использованием status.createSavepoint(). Я попробовал это, потому что я также подозревал, что ошибка существует, потому что транзакция передается от контроллера к службе и что мне нужно было запустить новую / вложенную транзакцию, чтобы избежать ее, но, как показывает ошибка, это невозможно в моем случае.

Или, может быть, проблема заключается в передаче транзакции?

Я предполагаю, что класс домена, используемый до.withTransaction, не имеет значения, или это так?

1 ответ

Решение

Это не закрытие, но я верю transaction имеет некоторую устаревшую ссылку на переменную внутри. Что если вы попытаетесь передать только замыкания, которые перечитывают свои объекты при выполнении? подобно

executeOptimisticTransaction {
  Something some = Something.get(id)
  some.properties = aMap
  some.save()
}

Я не думаю, что можно "обновить" объект, не перечитав его в Hibernate.

Да, не имеет значения, в каком классе вы вызываете.withTransaction().

Для примера обновления вычисленных итогов / рейтингов это дублирование данных, которое само по себе является проблемой. Я бы предпочел либо:

  1. создать (Quartz) задание, которое будет обновлять оценки на основе некоторого "грязного" флага - это может сэкономить некоторый ЦП БД за счет затрат времени на обновление;
  2. или сделать это в SQL или HQL, как Book.executeQuery('update Rating set rating=xxx') что будет использовать последний рейтинг. Если вы оптимизируете для большой нагрузки, вы все равно не будете делать все Groovy-way. Не сохраняйте объекты рейтинга в Grails, только читайте их.
Другие вопросы по тегам