I have been playing with caching with hibernate within the Play!
framework. There are 2 levels that what to talk about:
1. Hibernate cache plugin:
For now I have been using
http://code.google.com/p/hibernate-memcached/
just including it in the app/lib folder and adding the following to
the application.conf file:
hibernate.cache.provider_class=com.googlecode.hibernate.memcached.MemcachedCacheProvider
hibernate.cache.use_query_cache=true
hibernate.memcached.servers=
127.0.0.1:11211
hibernate.memcached.cacheTimeSeconds=300
I want to create a new hibernate cache provider that will utilize the
play.cache.Cache interface.
Obviously my current implementation is not ideal as the memcache
configuration (servers etc) are duplicated (once for play, once for
hibernate-memcached).
How do you think this would be best implemented? module?
seperately? in the play.cache / play.db / play.data package?
2. Hibernate 2nd level cache for models and Query Cache
I have general model and relationship (OneToMany etc) caching
accomplished via hibernate annotations
@org.hibernate.annotations.Cache(usage =
CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
Kinda sucks, as I don't think the JPA has any support for caching so
we wouldn't have to be tied to the hibernate implementation being
used. Any thoughts here?
Query Cache
So then I started wanting to use the hibernate query cache. I know
you could implement this functionality yourself { Check Cache, return,
Else Get / Save, return } but the complications come into play knowing
when to refresh the cached query if something was to be updated.
return User.find("order by _updated desc").setCacheable(true).fetch
();
Example. Say we wanted to get a list of the current users ordered by
name. We would currently do something like this.
public static List<User> findAllOrderByName()
{
return User.find("order by name").fetch();
}
Then we could add the calls to implement caching. As mentioned you
don't know that the cache should be refreshed when any other User is
saved. You could hook into the save() function (or postCommit) and
just clear the cache for this entry (and any others you have), but it
would be nice to just let hibernate take care of it.
So my current brute force implementation is (I saw how the underlying
hibernate session was obtained in the JpaSupport class):
public static List<User> findAllOrderByName()
{
Session session = ((HibernateEntityManager)JPA.em()).getSession();
Criteria criteria = session.createCriteria(User.class);
criteria.setCacheable(true);
criteria.addOrder(Order.desc("name"));
return criteria.list();
}
This will basically cache the list of ids for the users and then get
each user from the cache and never hit the database. It will also
invalidate the query cache entry when a user is saved.
This obviously ties the code to a hibernate implementation, so I was
thinking of different ways on how to implement this better.
This is the inteface I was thinking of, basically augmenting the
JPAQuery class to implement setCacheable() simply relying on the
underlying hibernate query cache. JPAQuery is already tied to
hibernate at this point so I think structurally its no worse.
public static List<User> findAllOrderByName()
{
return User.find("order by name").setCacheable(true).fetch();
}
Thoughts?
Sorry for the long post :-)