2

I got code like this:

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

Despite of the get and the refresh, I sometimes get an "org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)" when the save is executed.

It happens when I start to execute this method concurrently in multiple sessions. But then transaction 1 is definitely finished, this code is executed again for transaction 2 and it still fails! (I'm using a transaction service to re-execute transactions when they fail due to optimistic locking, see here).

How can that be although I get a "fresh" version from the DB?

Community
  • 1
  • 1
Jörg Brenninkmeyer
  • 3,304
  • 2
  • 35
  • 50
  • Does `MyDomainClass` have any cascade relationships, like `hasMany` or `belongsTo`? That might be the connected objects that get updated and saved in cascade. What class does StaleObjectStateException refer to? – Victor Sergienko Dec 08 '10 at 09:52
  • MyDomainClass does have hasMany and belongsTo relationships, but the Exception refers directly to MyDomainClass#id. – Jörg Brenninkmeyer Dec 08 '10 at 10:00
  • @Joe, your DomainClass has the version field? And you need it? – Gadonski May 06 '15 at 14:16

3 Answers3

1

This forum thread hints that you might need another Hibernate Session. What if you try a new session for a new transaction, like

Book.withNewSession{}
Victor Sergienko
  • 13,115
  • 3
  • 57
  • 91
  • Just tried it, this gives me an org.springframework.orm.hibernate3.HibernateSystemException: Illegal attempt to associate a collection with two open sessions; nested exception is org.hibernate.HibernateException: Illegal attempt to associate a collection with two open sessions – Jörg Brenninkmeyer Dec 08 '10 at 10:13
  • I believe that means that collection it refers to should be re-read too. – Victor Sergienko Dec 08 '10 at 10:28
  • I also tried to close the session before the withNewSession, which then causes and endless loop in the request... very strange. Maybe you're right about the collection, although I don't even know which collection it refers to. So long I'll go for my nasty workaround ;-). – Jörg Brenninkmeyer Dec 08 '10 at 10:57
  • 1. I don't see why do you need refresh() immediately after get(). That might be the cause of some `hasMany` collection getting stale. 2. If you wish to explicitly re-read the object, you can evict it from Hibernate cache beforehead: `sessionFactory.evict(MyDomainClass, myId); def myObject = MyDomainClass.get(myId)` – Victor Sergienko Dec 08 '10 at 11:08
  • The refresh() was just an additional measure because get() didn't work... I thought this would make sure that the domain object is really the most current one, independently of caching. This is where I feel that I don't know enough about grails, and I don't know where these things are thoroughly documented. Even the Grails 1.2 book stays on a surface level here. Anyway I'll try using evict() next! – Jörg Brenninkmeyer Dec 08 '10 at 11:48
1

I at least found a workaround - rolling back an empty transaction:

myDomain.withTransaction { status -> 
  status.setRollbackOnly()
}
Jörg Brenninkmeyer
  • 3,304
  • 2
  • 35
  • 50
0

you should use

MyDomainClass.lock(myId)

instead of

MyDomainClass.get(myId)
AndreyT
  • 1,449
  • 1
  • 15
  • 28