Hibernate 2.1.1, Oracle 8i, no mapping docs
I'm writing a JournallingInterceptor. It's SO close to being done, but I'm hitting a snag trying to audit changes to collections. I read this post:
http://forum.hibernate.org/viewtopic.ph ... r+auditing and decided to take a different approach.
Instead of looking at currentState and previousState arrays, I'm attempting to load and cache (in the interceptor) copies of all collections to be journalled (journalled classes, properties and collections are defined in an xml config file). So, in onLoad and onFlushDirty, I call cacheCollection for each journalled collection in the entity being loaded or flushed. This (shallow) copies the objects over into a new collection.
Later, in findDirty, I compare the current collection to the cached one to look for changes. I only need to detect additions and deletions, so this works fine.
However, there is a problem when the collections are lazy and not initialized... Here is the cacheCollection method:
Code:
/**
* @param entity the entity originally passed into Interceptor.onLoad() or onFlushDirty()
* @param jc journalling metadata about the entity (i.e. which properties/collections to journal)
*/
private void cacheCollection(Object entity, JournalledClass jc)
{
// if entity has a collection to be journalled
if( jc.getCollectionPropertyName() != null ) {
try {
// was using PropertyUtils before...didn't work. Switched to straight reflection just
// to be explicit.
StringBuffer propertyInitCap = new StringBuffer( jc.getCollectionPropertyName() ) ;
propertyInitCap.setCharAt( 0, Character.toUpperCase( propertyInitCap.charAt( 0 ) ) ) ;
Method m = entity.getClass().getMethod( "get" + propertyInitCap.toString(), new Class[0] ) ;
Collection currCollection = (Collection)m.invoke( entity, new Object[0] ) ;
// PROBLEM: currCollection is a HashSet! Not a special hibernate lazy proxy collection!
// if I actually casted entity into a DocumentBean and called the getReportCategories,
// the lazy collection IS returned.
// not initialized test
if( currCollection.size() == 0 ) {
try {
// does nothing since the collection is unknown to hibernate!
Hibernate.initialize( currCollection ) ;
// try to access elements to wake the collection up
currCollection.iterator() ;
} catch( HibernateException e ) {
log.warn( "Error initializing collection", e ) ;
return ;
}
}
// ArrayList( anotherList ) constructor was not copying the objects over... addAll works
ArrayList cachedCollection = new ArrayList() ;
cachedCollection.addAll( currCollection ) ;
collectionsMap.put( jc.getClass().getName() + "." + jc.getCollectionPropertyName(), cachedCollection ) ;
} catch( Exception e ) {
log.error( "Unable to access collectionProperty " + jc.getCollectionPropertyName() +
" for entity " + entity.getClass().getName() ) ;
}
}
}
Problem is that the collection returned by the reflection stuff about is NOT the lazy proxy collection, it is a plain old (empty) HashSet. Hibernate.initialize has no effect on this collection. BUT, if I cast the entity into a domain object and call the getter explicitly, I get back the magical lazy collection. Hibernate.initialize( collection ) works great on this collection.
Problem is, I can't cast here since this Interceptor shouldn't know anything about my domain model... I could require calling code to fully initialize all collections before flushing, but that would be very lame.
Am I up against a wall here? Any ideas on how to get around this problem? Maybe I just don't fully understand all the CGLIB proxy magic going on here. Thanks