I’m using Hibernate 4.3.11.Final with Spring 3.2.11.RELEASE. I’m confused as to why my cache eviction isn’t working. I have this set up in my DAO …
@Override
@Caching(evict = { @CacheEvict("main") })
public Organization save(Organization organization)
{
return (Organization) super.save(organization);
}
@Override
@Cacheable(value = "main")
public Organization findById(String id)
{
return super.find(id);
}
and here’s my Spring config …
<cache:annotation-driven key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="org.mainco.subco.myproject.util.CacheKeyGenerator" />
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheCacheManager"
p:cacheManager-ref="ehcache"/>
<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean"
p:configLocation="classpath:ehcache.xml"
p:shared="true" />
<util:map id="jpaPropertyMap">
<entry key="hibernate.show_sql" value="true" />
<entry key="hibernate.dialect" value="org.mainco.subco.myproject.jpa.subcoMysql5Dialect" />
<entry key="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory" />
<entry key="hibernate.cache.provider_class" value="org.hibernate.cache.EhCacheProvider" />
<entry key="hibernate.cache.use_second_level_cache" value="true" />
<entry key="hibernate.cache.use_query_cache" value="false" />
<entry key="hibernate.generate_statistics" value="true" />
<entry key="javax.persistence.sharedCache.mode" value="ENABLE_SELECTIVE" />
</util:map>
<bean id="sharedEntityManager"
class="org.springframework.orm.jpa.support.SharedEntityManagerBean">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
Yet in the below test, my entity is not getting evicted from the cache, which I know because the line with “hit count #3:” prints out “3” whereas the line with "hit count #2:” prints out “2”.
private net.sf.ehcache.Cache m_cache
@Autowired
private net.sf.ehcache.CacheManager ehCacheManager;
@Before
public void setup()
{
m_cache = ehCacheManager.getCache("main");
m_transactionTemplate = new TransactionTemplate(m_transactionManager);
} // setup
...
@Test
public void testCacheEviction()
{
final String orgId = m_testProps.getProperty("test.org.id");
// Load the entity into the second-level cache
m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
m_orgSvc.findById(orgId);
return null;
});
final long hitCount = m_cache.getStatistics().getCacheHits();
System.out.println("hit count #1:" + hitCount);
m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
final Organization org = m_orgSvc.findById(orgId);
System.out.println("hit count:" + m_cache.getStatistics().getCacheHits());
org.setName("newName");
m_orgSvc.save(org);
return null;
});
// Reload the entity. This should not incur a hit on the cache.
m_transactionTemplate.execute((TransactionCallback<Void>) transactionStatus -> {
System.out.println("hit count #2:" + m_cache.getStatistics().getCacheHits());
final Organization newOrg = m_orgSvc.findById(orgId);
System.out.println("hit count #3:" + m_cache.getStatistics().getCacheHits());
return null;
});
What is the right configuration to allow me to evict an entity from my second-level cache?
Edit: The CacheKeyGenerator class I referenced in my application context is defined below
public class CacheKeyGenerator implements KeyGenerator
{
@Override
public Object generate(final Object target, final Method method,
final Object... params) {
final List<Object> key = new ArrayList<Object>();
key.add(method.getDeclaringClass().getName());
key.add(method.getName());
for (final Object o : params) {
key.add(o);
}
return key;
}
}
As such I don’t have to define a “key” for each @Cacheable annotation which I prefer (less code). However, I don’t know how this applies to CacheEviction. I thought the @CacheEvict annotation would use the same key-generation scheme.
How to Evict a Cache? Spring provides two ways to evict a cache, either by using the @CacheEvict annotation on a method, or by auto-wiring the CacheManger and clearing it by calling the clear() method.
Cache eviction is a feature where file data blocks in the cache are released when fileset usage exceeds the fileset soft quota, and space is created for new files. The process of releasing blocks is called eviction. However, file data is not evicted if the file data is dirty.
This cache only works at a session level, meaning each session object caches data independently, so there is no sharing of cached data across sessions, and the cached data is deleted when the session closes. This makes the cache only useful for repeated queries in the same session.
I have re-written the CodeKeyGenerator
as below. This will make a key based on the parameter you send. If it is a string (In case of id), It will use it as it is. If it is a Organization
object, It gets the id from that object and use it for the key. This way you don't need to rewrite your code in all places. (Only change is you need to replace your CacheKeyGenerator
with the below code. )
public class CacheKeyGenerator implements KeyGenerator
{
@Override
public Object generate(final Object target, final Method method,
final Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(o.getClass().getName());
sb.append(method.getName());
if (params[0].getClass().getName() == "Organization" ) {
sb.append(((Organization) params[0]).id);
}
else if (params[0].getClass().getName() == "java.lang.String" ) {
sb.append(params[0].toString());
}
return sb.toString();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With