Hibernate version: 3.0 final release from today, 4/1/2005
Mapping documents:
The collection I'm seeing this problem with is mapped like this (from a PersistentUser class):
Code:
<!-- uni-directional many-to-many association to SecurityRight -->
<set
name="securityRightsGranted"
lazy="true"
cascade="none"
table="SystemUser2SecurityRightGranted"
>
<cache usage="nonstrict-read-write"/>
<key column="user_id"/>
<element
type="com.eplus.user.model.SecurityRight"
column="secrgh_id"
not-null="true"
/>
</set>
Code between sessionFactory.openSession() and session.close():Well, what it's doing is this (this is all handled by frameworks):
It loads the User object. The web framework is taking web request parameters and converting the String[] of identifiers into SecurityRight objects. It's getting the Set back from the User and calling clear() on it to empty it, then adding the SecurityRight objects created from the request params into it, so it's using the same Set. SecurityRight is a UserType implementing the persistent enum pattern and does readResolve() and assemble() to make sure it always gives back one of the statically registered instances, so it should end up putting back in the same instances.
Full stack trace of any exception that occurs:Code:
[ERROR] JDBCExceptionReporter - Violation of PRIMARY KEY constraint 'SystemUser2SecurityRightDenied_PK'. Cannot insert duplicate key in object 'SystemUser2SecurityRightDenied'.
[ERROR] AbstractFlushingEventListener - Could not synchronize database state with session <org.hibernate.exception.ConstraintViolationException: could not insert collection rows: [com.eplus.user.model.PersistentUser.securityRightsDenied#U1]>org.hibernate.exception.ConstraintViolationException: could not insert collection rows: [com.eplus.user.model.PersistentUser.securityRightsDenied#U1]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:63)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.persister.collection.AbstractCollectionPersister.insertRows(AbstractCollectionPersister.java:1051)
at org.hibernate.action.CollectionUpdateAction.execute(CollectionUpdateAction.java:48)
Name and version of the database you are using:SQLServer 2000
The generated SQL (show_sql=true):Hibernate: delete from SystemUser2SecurityRightDenied where user_id=? and secrgh_id=?
Hibernate: insert into SystemUser2SecurityRightDenied (user_id, secrgh_id) values (?, ?)
Debug level Hibernate log excerpt:don't have that on...
Anyway, I've been debugging into it, and the weird part is that when it does the clear() it calls write() on the PersistentSet, which tells it to initialize(), which tells the Session to initialize the collection. This calls initializeCollectionFromCache() on the DefaultInitializeCollectionEventListener, which eventually calls postInitialize() on the CollectionEntry. During postInitialize() it calls getSnapshot(), which is where things get weird. During the call to snapshot() on PersistentSet, it does this:
Code:
while ( iter.hasNext() ) {
Object copied = persister.getElementType().deepCopy( iter.next(), entityMode, persister.getFactory() );
clonedSet.put(copied, copied);
}
The weird part is that this copied object is null. There was only one item in the collection to begin with, and the snapshot ends up being a map with one entry, from null to null.
This is a problem because it doesn't realize that something has been removed in the clear() and, when the SecurityRight gets added back by the type converter in the web framework, it thinks the item is new and tries to save it. At least, that's the best I can figure out, and would explain the referential integrity exception.
This worked with Hibernate 2.1.8 until today when I'm trying to switch over. What can I do? I assume it's a problem with the clear(), but the fact that the snapshot ends up being null -> null during the clear is troublesome....