0
votes

For a simple Post-Tag @ManyToMany scenario like this one @ManyToMany inconsistent data on both side problem

It seems that hibernate's 2nd level cache doesn't know the time to update the aggregation function (ex: count ) like this one :

public int getPostCountByTag(Tag tag)
{
  Session session = (Session) em.getDelegate();
  Criteria c = session.createCriteria(Post.class);

  c.createCriteria("tags")
  .add(Restrictions.eq("id", tag.getId()));

  c.setProjection(Projections.rowCount());
  c.setCacheable(true);
  int num = ((Long) c.uniqueResult()).intValue();
  return num;
}

And hibernate also doesn't know it's time to update a list like this one :

public List<Post> getPostsByTag(Tag tag, int start, int count)
{
  Session session = (Session) em.getDelegate();
  Criteria c = session.createCriteria(Post.class);

  c.createCriteria("tags")
   .add(Restrictions.eq("id", tag.getId()));

  c.addOrder(Order.desc("created")); break;
  c.setFirstResult(start);
  c.setMaxResults(count);
  c.setCacheable(true);

  return c.list();
}

Each time I add/remove a Tag to/from a Post , these count and list remain unchanged . I know maybe it's because they are cached by c.setCacheable(true) , and I have to manually evict the cache or set the timeout shorter.

But , I wonder if there is any better / smarter way for hibernate to auto detect the cache eviction timing ?

In the example , there are just two methods that are affected by tag's add/remove , but as methods grows , the cache eviction management will become very cumbersome and error-prone . for example :

addTag(Post post , Tag tag)
{
  post.addTag(tag);
  postDao.update(post);
  EntityManagerFactoryUtils.getTransactionalEntityManager(emf).flush();

  evict_cache_in_getPostsByTag();
  evict_cache_in_getPostCountByTag();
  evict_cache_in_getPostsByTagAndBlah();
  evict_cache_in_getPostsByTagAndBlahBlah();
  evict_cache_in_getPostsByTagAndBlahBlahBlah();
}

removeTag(Post post , Tag tag)
{
  post.removeTag(tag);
  postDao.update(post);
  EntityManagerFactoryUtils.getTransactionalEntityManager(emf).flush();

  evict_cache_in_getPostsByTag();
  evict_cache_in_getPostCountByTag();
  evict_cache_in_getPostsByTagAndBlah();
  evict_cache_in_getPostsByTagAndBlahBlah();
  evict_cache_in_getPostsByTagAndBlahBlahBlah();
}

This is terrible !!!

I wonder how to solve this n x m problem ? (n is 2 (add/remove) , m is 5 (evicts) , in this sample )

By the way , I found hibernate's cache.evictQueryRegions() seems NOT work at all . I have to manually give each query cache a name (cache region) , and specify which cache region to evict by cache.evictQueryRegion("getTagPostsCount"); , Did I miss anything ? or is it Hibernate's bug ? (updated : after upgrading to Hibernate 3.6.0.Final , the problem is solved , it seems it is 3.5.6's bug)

Hibernate 3.5.6-Final , hibernate-jpa-2.0 , ehcache-1.5.0 , Spring-3.0.4

1

1 Answers

0
votes

have configured the hibernate to use second level cache, as in are you using ehcache for achieving l2 cache. because if you are using that, then you can add the time after which cache will be updated from Database(synchronize with database),

this is what i am using in my application, may be this can help you..

<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"/>

after the expire time the cache will be automatically updated.