3

I am using spring-4.3.

@Cacheable("productCategories")
@Override
public ProductCategory get(Object id) throws BeanNotFoundException {
    return super.get(id);
}

@Cacheable("productCategoryChildren")
public List<ProductCategory> getChildren(String parentId) {
    return productCategoryDao.getChildren(parentId);
}

First i invoke get("100") and return a entity, and then invoke getChildren("100"), i get a error java.lang.ClassCastException: com.jxs.ms.entity.ProductCategory cannot be cast to java.util.List. Is this two method use the same cache to store value?

1
  • No, the problem has nothing to do with caching! As the cacheName is different in both of the methods, so it cannot use same cache. Have you checked the value which is being returned from productCategoryDao.getChildren(parentId) ? Commented Jun 27, 2016 at 5:51

2 Answers 2

2

I had a similar issue, but in my case we were using spring with memcached. Our problem was that the CacheConfiguration defaults the useNameAsKeyPrefix property to false.

The key to understand is that the underlying cache for both named caches uses the same map. In this case id and parentId can end up being the same cache key. Even though the caches are named differently in the spring cache abstraction, the underlying cache manager stores them all in the same place. So the first request to get("1234") populates the cache with a ProductCategory for the given id, "1234". Then when getChildren("1234") is called, the cache manager does a lookup ignoring the cache name and just looking for key, "1234", which it finds and returns a ProductCategory. Because the spring proxy bean is expecting a List<ProductCategory>, you end up with a ClassCastException.

Updating the config like this, fixed my problem,:

<bean id="defaultMemCacheConfiguration" class="com.google.code.ssm.providers.CacheConfiguration">
    ...
    <property name="useNameAsKeyPrefix" value="true"/>
</bean>
1

I found the problem in Joshua White's Blog.

Warning: When dealing with other caching solutions, Spring’s CacheManger usually contains a map of Cache (each implementing map like functionality) implementations that are backed by separate caches. Using the default RedisCacheManager configuration, this is not the case. Based on the javadoc comment on the RedisCacheManager, its not clear if this is a bug or simply incomplete documentation. “…By default saves the keys by appending a prefix (which acts as a namespace).”

While the DefaultRedisCachePrefix which is configured in the RedisCacheManager certainly supports this, it is not enabled by default. As a result, when you ask the RedisCacheManager for a Cache of a given name, it simply creates a new Cache instance that points to the same database. As a result, the Cache instances are all the same. The same key will retrieve the same value in all Cache instances.

As the javadoc comment alludes to, prefixs can be used to setup client managed (Redis doesn’t support this functionality natively) namespaces that essentially create “virtual” caches within the same database. You can turn this feature on by calling redisCacheManager.setUsePrefix(true) either using the Spring XML or Java configuration.

@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
    RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
    cacheManager.setDefaultExpiration(3600 * 2);
    cacheManager.setUsePrefix(true);//this solved my problem
    return cacheManager;
}
1
  • I disagree! Can you post if the above reference did solved your issue? Commented Jun 27, 2016 at 6:02

Not the answer you're looking for? Browse other questions tagged or ask your own question.