In some piece of our code we are using HibernateDaoSupport's setCacheQueries() method. At first we had a function开发者_运维知识库 getByGroupId()
that just called setCacheQueries(true)
, but when doing integration testing this caused Hibernate to throw "duplicate exceptions".
So I googled around a bit and saw that many used a pattern where they enabled cache before doing a query and disabling it afterwards. I then tried disabling caching after the query, and the duplication bug disappeared. Now I am wondering what this pattern really does? The code below is modeled after this pattern.
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class MyDao extends HibernateDaoSupport{
public List getByGroupId(Long groupId) {
getHibernateTemplate().setCacheQueries(true);
List result = getHibernateTemplate().find(
"from Selection where groupId = ? order by sortOrder ASC", groupId);
getHibernateTemplate().setCacheQueries(false);
return result;
}
}
I am unsure on how Spring and Hibernate works together here. If setCacheQueries(false)
would empty all cached queries it would make no sense, but if it just disables caching of later queries (until setCacheQueries(true)
is called) it would make a little more sense.
- Is this pattern of turning on/off caching before and after doing queries normal?
- Does it work (i.e. are the queries cached)?
- Any idea why it would cause Hibernate to throw exceptions about duplicate entries when not calling
setCacheQueries(false)
after the query?
When the HibernateTemplate's cacheQueries
property is true, it automatically makes each Query
or Criteria
executes cacheable. I.e. it calls Query.setCacheable(true) and Criteria.setCacheable(true) before executing the query/criteria.
So, your pattern consists in fact in making the query you're going to execute cacheable, and then reset the flag to false so that the next query is not made cacheable.
The problem is that if the HibernateTemplate is used by multiple threads, the results of using this pattern are indeterminate. You might have a thread set the flag to true, and then another one immediately reset it to false before the first thread has the time to execute its query. And since the access to the property is not synchronized, you might have visibility problems as well.
I would use HibernateTemplate.setCacheable(true)
only at the creation of the HibernateTemplate, to make sure that all the queries it executes are cacheable. If you don't want it, then use two different HibernateTemplate instances (one with the cache enabled, and the other without), or use the Hibernate API directly when you want a different caching behavior.
Regarding your exception, without knowing the use-case, the exact exception and its stack trace, it's hard to diagnose.
JB's answer is right on the money. In addition to that, here's how you can enable query caching per query instead of globally for the whole template:
public List findBySomething(final int something) {
return getHibernateTemplate().execute(new HibernateCallback<List>() {
@Override
public List doInHibernate(Session session) {
Query query = session.createQuery("from Something where something = :something");
query.setParameter("something", something);
query.setCacheable(true);
return query.list();
}
});
}
精彩评论