I have an entity with a property that is a Map<String, String> and uses an version # for optimistic locking.
When I load an instance of this class using
Code:
session.load(ClassWithMap.class, idValue)
and resave the object with saveOrUpdate() the version # does not change. This is good, because the object HAS NOT changed.
BUT, if I retrieve this same object using a Query or Criteria search and then save it with saveOrUpdate(), the version # is incremented. This appears to be from CollectionType.isEqual(Object, Object, EntityMode) using the '==' operator to see that the instance's collection is not just equal but actually the same collection instance. I traced through the following (Hibernate 3.2.7.ga):
Code:
TypeFactory.findDirty[line 599]
properties[i].getType().isDirty( previousState[i], currentState[i], includeColumns[i], session )
CollectionType[line 291].isDirty(…)
CollectionType[line 284].isDirty(…)
return isOwnerVersioned( session ) is true && super.isDirty( old, current, session );
AbstractType.isDirty[line 70]
return !isSame( old, current, session.getEntityMode() );
AbstractType.isSame[line 103]
return isEqual(x, y, entityMode);
CollectionType.isEqual[line 90]
return x == y
|| ( x instanceof PersistentCollection && ( (PersistentCollection) x ).isWrapper( y ) )
|| ( y instanceof PersistentCollection && ( (PersistentCollection) y ).isWrapper( x ) );
Here's the class mapping - note the 'lazy="false"' just in case that's important:
Code:
<class name="eg.ClassWithMap" table="test_table" lazy="false">
<id name="objectId" column="object_id">
<generator class="sequence">
<param name="sequence">test_id_sequence</param>
</generator>
</id>
<version name="optimisticVersion" column="instance_version" unsaved-value="negative" />
<property name="identifierName" column="identifier_name" />
<property name="descriptiveName" column="descriptive_name" />
<map name="parameterValues" table="map_parms"
lazy="false" cascade="all" optimistic-lock="false">
<key column="object_id" />
<map-key column="p_name" type="string" />
<element column="p_value" type="string" />
</map>
</class>
And here's a JUnit that demonstrates a couple of oddities:
Code:
public void testHibernateQueryIncrementsVersionProblem() throws Exception
{
Map<String, String> parms = new HashMap<String, String>();
ClassWithMap lc = new ClassWithMap("lc_test","LC Test",parms);
Session session = SessionFactoryUtils.getNewSession(sessionFactory);
session.beginTransaction();
Serializable lcId = session.save(lc);
logger.info("saved ClassWithMap to id: " + lcId);
session.getTransaction().commit();
assertEquals("version", 0, lc.optimisticVersion);
for (int index = 0; index < 20; index ++) {
session = SessionFactoryUtils.getNewSession(sessionFactory);
session.beginTransaction();
Query query = session.createQuery("from ClassWithMap as theObject where theObject.objectId = :objId");
query.setParameter("objId", lc.objectId);
List<ClassWithMap> persistentVersions = query.list();
session.getTransaction().commit();
SessionFactoryUtils.releaseSession(session, sessionFactory);
assertEquals("there's only 1", 1, persistentVersions.size());
ClassWithMap queriedVersion = (ClassWithMap) persistentVersions.get(0);
assertEquals("The just queried version", i, queriedVersion.optimisticVersion);
session = SessionFactoryUtils.getNewSession(sessionFactory);
session.beginTransaction();
session.saveOrUpdate(queriedVersion);
session.getTransaction().commit();
SessionFactoryUtils.releaseSession(session, sessionFactory);
assertEquals("After saving, it has mistakenly incremented version #", index +1, queriedVersion.optimisticVersion);
}
session = SessionFactoryUtils.getNewSession(sessionFactory);
session.beginTransaction();
ClassWithMap rehy2 = (ClassWithMap)session.load(ClassWithMap.class, lc.objectId);
session.getTransaction().commit();
SessionFactoryUtils.releaseSession(session, sessionFactory);
// this should fail - but it doesn't
assertEquals("now the version's got nothing to do with the value stored in the db - should be 20", 0, rehy2.optimisticVersion);
session = SessionFactoryUtils.getNewSession(sessionFactory);
session.beginTransaction();
session.saveOrUpdate(rehy2);
session.getTransaction().commit();
SessionFactoryUtils.releaseSession(session, sessionFactory);
// this should fail too
assertEquals("it still has the wrong version after saving", 0, rehy2.optimisticVersion);
}
What am I overlooking? What am I missing?
Thanks,
-- Chris