Hibernate version: 3.2.5
Mapping documents: will post if necessary
Code between sessionFactory.openSession() and session.close(): not relevant (using spring HibernateTemplate methods)
Full stack trace of any exception that occurs: will post if necessary
Name and version of the database you are using: Oracle 10
The generated SQL (show_sql=true): will post if necessary
Debug level Hibernate log excerpt: not available
I know you normally want all the above info up front, but I will be precise asking my question, and if I need to provide further info I will. I have read many articles, posts, I've read the relevant portions of the manual, and I've debugged and reduced this problem down to a very direct set of questions.
Dozens of exceptions thrown on a weekly basis... all
StaleObjectState and
NonUniqueObject. We're using Spring's
org.springframework.orm.hibernate3.HibernateTemplate, calling its methods for session operations. The JavaDoc for that class states that it
Quote:
"provides Hibernate Session handling such that neither the HibernateCallback implementation nor the calling code needs to explicitly care about retrieving/closing Hibernate Sessions, or handling Session lifecycle exceptions."
The problem, I believe, is that the app is a Java desktop app served via JNLP. So each user has their own session/cache, etc., which essentially (I think I understand after reading the manual) robs Hibernate of one of its greatest strengths. Optimistic locking is in use via <version> tags. All fields in data objects are non-primitives. All mappings supply unsaved-value specifics where appropriate. Hibernate appears to be doing exactly what it's designed to do with respect to optimistic locking, and like the manual says, these errors are designed to represent unrecoverable situations.
For the time being, however, I'm supposed to fix this or suggest a redesign, but I'm new to Hibernate so I'm trying to learn a little more from the forum. The unique nature of this app makes it so that most of the posts I read provide only tangential information. They're experiencing these conditions for normal reasons under normal Hibernate scenarios (batch updates, constraint violations, etc.) I'm experiencing them because this multi-user, nobody's-session-knows-about-each-other scenario may be a slight misuse of Hibernate, and may (theory) mean I'll have to do more direct session manipulation than would normally be suggested.
So here's what's happening. We're calling (throughout the software):
Code:
HibernateTemplate.find(String, Object[])
HibernateTemplate.saveOrUpdate(Object)
HibernateTemplate.merge(Object)
(merge is tried as a recovery attempt from exceptions thrown by saveOrUpdate... yes I know that is explicitly discouraged... I'm new on this app and I'm just trying to understand)
The
find(String, Object[]) call often produces:
Code:
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException:
Object of class [foo.bar.CategoryData] with identifier [881791]:
optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [foo.bar.CategoryData#881791]
with
log output (from Hibernate) that says:
Code:
ERROR (performExecutions:301) Could not synchronize database state with session
The
saveOrUpdate(Object) call sometimes produces the
same exception (StaleObj) and the
same log output, and other times it produces:
Code:
org.hibernate.NonUniqueObjectException:
a different object with the same identifier value was already associated with the session: [foo.bar.CategoryData#851289]
In that case, the code immediately tries to recover with a
merge(Object), which produces again (you guessed it):
Code:
org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException:
Object of class [foo.bar.CategoryData] with identifier [881791]:
optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException:
Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [foo.bar.CategoryData#881791]
with
log output (from Hibernate) that says:
Code:
ERROR (performExecutions:301) Could not synchronize database state with session
My questions:
1) What exactly does the phrase "Could not synchronize..." mean in terms of how it determines that? I can see it's coming from AbstractFlushingEventListener, which means the operations we're performing are causing a flush. Is it simply that in trying to persist its state, the StaleObjectStateException is marking an objection to a version issue, and NonUniqueObjectException is saying the code is violating session-scoped identity? Is that always exclusively a problem with equals() and hashcode() implementations, or are there other things we could be doing wrong?
2) What are my options under the StaleObjectState circumstances? I've read tons of posts about the "evict" method? Does that work? If I have version 1 in the session, the DB has version 4, and a saveOrUpdate fails, can I simply evict that Stale instance and then call saveOrUpdate again?
3) If I did evict the stale instance, how do I avoid overwriting what some other user (another instance of the app, Hibernate, etc) wrote to the DB?
4) What are my options under the NonUniqueObjectException scenarios? I don't understand why saveOrUpdate would care if there's a different object with the same identifier? Isn't that the session's copy of the object I want to update, or is it actually saying it's a DIFFERENT object that identifies the same as the one I have a reference to?
I'll stop there. These are the most basic questions I have.
Any help would be greatly appreciated.