I am working with hibernate 2.1 and am having an issue with successfully rolling back objects generated identifier values when a transaction rolls back.
I should mention that our system uses "short-lived/fleeting" sessions, only performing work within a transaction when abosolutely neccessary.
I will explain what my goal is at a high level first.
Our system has several points at which it validates the state of persisted objects. One of those points, which we call "Save" began causing us grief when it came to reusing objects (that are maintained using a wizard on the client) that failed validation in this "Save" step.
So we have an object that has some invalid state that we do not want to persist to the store, but we are already inside our transaction. When the system finds this object is invalid, it throws a runtime exception that is gracefully handled, and the transaction is rolled back. As mentioned, our original problem was that any objects saved within the transaction so far will retain their generated ids and concurrency values (which is not correct). Unfortunately hibernate leaves the rolling back of these values to the developer, which wasn't too much hassle to implement.
Everything seems to work correctly in our codebase up until a certain point. One of the objects that is to be flushed is a newly created object, and therefore will have a identifier value of null initially. It has several one-to-many collections mapped.
The transaction rolls back, and our system reassigns all the objects identifier values and concurrency values to a snapshot taken before the save was attempted. From debugging, I can see all of the objects are in a state which we want them after the rollback. When the incorrect data is reentered correctly by the user, and the system attempts to save the objects again (this time using identifier values taken prior to the first save), hibernate is throwing the following exception.
Code:
Caused by: net.sf.hibernate.HibernateException: Don't dereferemce a collection with cascade="all-delete-orphan": com.initech.core.Order.orderItems
at net.sf.hibernate.impl.SessionImpl.prepareCollectionForUpdate(SessionImpl.java:3002)
at net.sf.hibernate.impl.SessionImpl.updateReachableCollection(SessionImpl.java:2939)
at net.sf.hibernate.impl.FlushVisitor.processCollection(FlushVisitor.java:32)
at net.sf.hibernate.impl.AbstractVisitor.processValue(AbstractVisitor.java:69)
at net.sf.hibernate.impl.AbstractVisitor.processValues(AbstractVisitor.java:36)
at net.sf.hibernate.impl.SessionImpl.flushEntity(SessionImpl.java:2637)
at net.sf.hibernate.impl.SessionImpl.flushEntities(SessionImpl.java:2503)
at net.sf.hibernate.impl.SessionImpl.flushEverything(SessionImpl.java:2298)
at net.sf.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:1821)
at net.sf.hibernate.impl.SessionImpl.getQueries(SessionImpl.java:1578)
at net.sf.hibernate.impl.SessionImpl.find(SessionImpl.java:1543)
at net.sf.hibernate.impl.QueryImpl.list(QueryImpl.java:49)
at au.com.essential.mansys.bl.LimitedQueryCallback.executeQuery(AbstractSpringHibernateDao.java:1303)
at au.com.essential.mansys.bl.LimitedNamedQueryCallback.doInHibernate(AbstractSpringHibernateDao.java:1315)
at org.springframework.orm.hibernate.HibernateTemplate.execute(HibernateTemplate.java:312)
... 50 more
Looking at the source, and attaching and debugging it I can see that hibernate is attempting to prepare flushing the one-to-many mapped collection (which incidentally contains no objects).
Code:
if ( entry.loadedPersister!=null || entry.currentPersister!=null ) { // it is or was referenced _somewhere_
if (
entry.loadedPersister!=entry.currentPersister || // if either its role changed,
!entry.currentPersister.getKeyType().equals(entry.loadedKey, entry.currentKey) // or its key changed (for nested collections)
) {
// do a check
if (
entry.loadedPersister!=null &&
entry.currentPersister!=null &&
entry.loadedPersister.hasOrphanDelete()
) {
throw new HibernateException("Don't dereferemce a collection with cascade=\"all-delete-orphan\": " + coll.getCollectionSnapshot().getRole());
}
The exception is getting thrown at the bottom of the source above, it seems because the entry.loadedKey value is different from the entry.currentKey value. I am unsure as to how I can work around this requirement that those two values are equal. Another strange thing is that the loadedKey value is the next identifier value from the sequence in the table, and the currentKey value is the one after that.
It seems here that hibernates inner workings (the second level cache maybe) are remembering the previously assigned and generated identifier value and the snapshot isnt getting pushed down deep enought for the comparison to return true.
Does anyone know what could be happening here?