Good <time-of-day>!
I'm writing a custom "lazy materialized" multimap collection for
Hibernate. The main reason I'm doing this is that I want to use
2nd-level cache.
So far, I've found several problems with the way caching is implemented
in Hibernate:
1. Collections are not cached unless they are completely initialized.
That kind of kills the possibility of cached extra lazy collections.
2. Collection loading can be VERY slow. The "initializeFromCache" method
of the built-in Hibernate collections looks something like:
========
public void initializeFromCache(CollectionPersister persister,
Serializable disassembled, Object owner)
throws HibernateException {
Serializable[] array = ( Serializable[] ) disassembled;
int size = array.length;
beforeInitialize( persister, size );
for ( int i = 0; i < size; i++ ) {
list.add( persister.getElementType().assemble( array[i],
getSession(), owner ) );
}
}
========
If the collection elements are pushed out of the cache (and its a quite
probable situation with large collections) - we'll have N+1 selects problem.
3. Mass operations API for caches is not used in Hibernate. Some caches
support bulk loading APIs (usually in forms like 'getAll(Collection
keys)'). We can greatly speedup the 2nd-level caches by using these APIs
(when available, of course).
I propose the following solutions:
1. I think it's possible to add a manual collection cache entry update
in the postAction() callback. And it'll probably work.
2. This one is much harder to fix. I'm planning to add a special flag
"onlyCached" to the LoadEvent and add the corresponding functionality to
the DefaultLoadEventListener. So I'll have something like:
========
public void initializeFromCache(CollectionPersister persister,
Serializable disassembled, Object owner)
throws HibernateException {
Serializable[] array = ( Serializable[] ) disassembled;
int size = array.length;
Object [] loaded=new Object[size];
List notInCache=new ArrayList();
beforeInitialize( persister, size );
for ( int i = 0; i < size; i++ ) {
loaded[i]=session.tryToLoadFromCache(array[i], owner);
if (loaded[i]==null)
notInCache.add(array[i]);
}
Iterator entities=getSession().createCriteria(
persister.getElementType().getName())
.add(Restrictions.in("id",notInCache))
.iterate();
for(int i=0; i < size; i++)
{
if (loaded[i]!=null)
list.add(loaded[i]);
else
list.add(entities.next());
}
}
========
3. This is a fairly large task, which requires changing some of
Hibernate fundamentals (like LoadEvent). But I think I can do it.
Any thoughts? Am I stupidly missing some obvious flaw?
--
With respect,
Alex Besogonov (cyberax(a)staffdirector.net)