Почему в grails сразу после обновления возникает исключение StaleObjectStateException?

У меня такой код:

def myObject = MyDomainClass.get(myId)
myObject.refresh()
myObject.myProperty = myValue
myObject.save(flush:true, failOnError:true)

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

Это происходит, когда я начинаю выполнять этот метод одновременно в нескольких сеансах. Но тогда транзакция 1 определенно завершена, этот код снова выполняется для транзакции 2 и все еще не выполняется! (Я использую службу транзакций для повторного выполнения транзакций, когда они терпят неудачу из-за оптимистичной блокировки, см. здесь).

Как такое может быть, если я получил "свежую" версию из БД?


person Jörg Brenninkmeyer    schedule 08.12.2010    source источник
comment
Есть ли у MyDomainClass каскадные отношения, такие как hasMany или belongsTo? Это могут быть связанные объекты, которые обновляются и сохраняются в каскаде. К какому классу относится StaleObjectStateException?   -  person Victor Sergienko    schedule 08.12.2010
comment
MyDomainClass действительно имеет отношения hasMany и ownTo, но исключение относится непосредственно к MyDomainClass # id.   -  person Jörg Brenninkmeyer    schedule 08.12.2010
comment
@Joe, в вашем DomainClass есть поле версии? А оно тебе нужно?   -  person Gadonski    schedule 06.05.2015


Ответы (3)


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

Book.withNewSession{}
person Victor Sergienko    schedule 08.12.2010
comment
Только что попробовал, это дает мне org.springframework.orm.hibernate3.HibernateSystemException: Незаконная попытка связать коллекцию с двумя открытыми сеансами; вложенное исключение - org.hibernate.HibernateException: незаконная попытка связать коллекцию с двумя открытыми сеансами - person Jörg Brenninkmeyer; 08.12.2010
comment
Думаю, это означает, что следует перечитать и сборник, о котором идет речь. - person Victor Sergienko; 08.12.2010
comment
Я также пытался закрыть сеанс перед withNewSession, что затем вызывает бесконечный цикл в запросе ... очень странно. Может быть, вы правы насчет коллекции, хотя я даже не знаю, к какой коллекции она относится. Пока я буду искать обходной путь ;-). - person Jörg Brenninkmeyer; 08.12.2010
comment
1. Я не понимаю, зачем вам refresh () сразу после get (). Это могло быть причиной того, что какая-то hasMany коллекция устарела. 2. Если вы хотите явно перечитать объект, вы можете удалить его из кеша Hibernate перед заголовком: sessionFactory.evict(MyDomainClass, myId); def myObject = MyDomainClass.get(myId) - person Victor Sergienko; 08.12.2010
comment
Обновление () было просто дополнительной мерой, потому что get () не сработало ... Я думал, что это позволит убедиться, что объект домена действительно является самым актуальным, независимо от кеширования. Именно здесь я чувствую, что недостаточно знаю о Grails, и я не знаю, где эти вещи тщательно задокументированы. Здесь даже книга Grails 1.2 остается на поверхности. В любом случае, я попробую использовать evict () дальше! - person Jörg Brenninkmeyer; 08.12.2010

Я по крайней мере нашел обходной путь - откат пустой транзакции:

myDomain.withTransaction { status -> 
  status.setRollbackOnly()
}
person Jörg Brenninkmeyer    schedule 08.12.2010

вы должны использовать

MyDomainClass.lock(myId)

вместо

MyDomainClass.get(myId)
person AndreyT    schedule 06.05.2015