Как преодолеть исключение StaleObjectStateException в Grails Service - PullRequest
2 голосов
/ 07 декабря 2010

Я ввел 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 Ответ

1 голос
/ 07 декабря 2010

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

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

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

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

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

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