I too have the same problem. I was looking around docs for some hints when I saw this, so have made the assumption that if steve is stumped (at least temporarily :-) that the fix must be more than a special method call, etc. Hope I'm correct!
Here is my best idea of what is going on.
The error will occur when you attempt to save a new object in a transaction, and that transaction fails for whatever reason. Hibernate keeps score of what passes through, so although the object is still transient (the flush failed), the object will now have an ID.
So, when you go to fix the error with the object, and attempt to re-save it, hibernate will fail because it sees the ID, assumes the object has been persisted, and shoots into its update routines. The update routines, however, expect the object to be in the database, and throw the error tomsedge2 reports when they do not find it.
So my attempted workarounds:
My first thought is to work with what the hibernate API gives me. I have a DAO layer that abstracts all this, so I record what is going on, and if something fails, I simply remove the ID on the object.
Hibernate keeps track of that too, however, and throws an error complaining about the id of my object changing to Null when I attempt to commit my transaction.
So I dove into hibernate, and this is what I have so far, which is working for my small test case. (test case is: start txn, save object I know will fail due to db constraints, rollback txn, fix errors in object, start txn, save object, commit txn)
You will find this function in net.sf.hibernate.impl.SessionImpl
Code:
private Serializable doSave(
final Object object,
final Serializable id,
final ClassPersister persister,
final boolean useIdentityColumn,
final Cascades.CascadingAction cascadeAction,
final Object anything)
throws HibernateException {
if ( log.isTraceEnabled() ) log.trace( "saving " + MessageHelper.infoString(persister, id) );
final Key key;
if (useIdentityColumn) {
// if the id is generated by the database, we assign the key later
key = null;
}
else {
key = new Key(id, persister);
Object old = getEntity(key);
if (old!= null) {
EntityEntry e = getEntry(old);
if (e.status==DELETED) {
forceFlush(e);
}
else {
throw new NonUniqueObjectException( id, persister.getMappedClass() );
}
}
persister.setIdentifier(object, id);
}
// Sub-insertions should occur before containing insertion so
// Try to do the callback now
if ( persister.implementsLifecycle() ) {
log.debug("calling onSave()");
if ( ( (Lifecycle) object ).onSave(this) ) {
log.debug("insertion vetoed by onSave()");
return id;
}
}
// ################# Begin Fix ##########################
/* A hack to attempt & fix the save & fail / save again error.
* The general idea is to remove every trace of the object being inserted if
* something goes wrong.
*
* Possible problems with this fix are:
* 1. I am not a hibernate expert, this could break a great many things
* 2. I believe this code relies on the user of the hibernate API to rollback
* (which isn't much to ask, it is required according to the docs)
*
* You *must* re-call save, or saveOrUpdate on the object in the new transaction.
*/
try {
return doSave(object, key, persister, false, useIdentityColumn, cascadeAction, anything);
} catch (HibernateException t) {
this.removeEntity(key);
this.removeEntry(object);
persister.setIdentifier(object, null);
throw t;
}
// ################# End Fix ##########################
}
I do not expect this to be the be-all-to-end-all solution, but at least I can shed a little more light on the subject and some possible fixes.
Really, my "gut" reaction is that something other than ID's should be used to determine if an object is transient or persistent. It seems that hibernate implements methods to keep track of what happens to a specific object (hence it knowing that I changed the ID), perhaps those same methods could be used to determine if an object is transient or not.
That is all I have for the moment. I have never used Jira, etc., so I will look into that tonight when I get home and see if I can start something on that end as well.