Today I needed some performance improvements on a application that makes quite a few calls to a database. My default statement is not to implement performance improvements unless there is a issue. Well it just happened.
I did not wanted to change any of the existing code so what to do. In Spring 3.x the added a caching mechanism. Implementing this is simple:
@Configuration
@EnableCaching
public class SpringConfiguration {
/**
* Name of the available Cache
*/
public final static String CORE_CACHE = "default";
/**
* Creates a in memory cache using a concurrent HasMap.
*
* @return cache manager
*/
@Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager(CORE_CACHE);
}
@Component
public class MyDataImpl implements MyData {
@PersistenceContext(unitName = "persistenceUnit")
private EntityManager entityManager;
@Cacheable(value = SpringConfiguration.CORE_CACHE)
@Override
public Map<String, Code> getCodes() {
final TypedQuery<Map<String, CodeTypes>> codesQuery
= entityManager.createNamedQuery(CODE_QUERY,
Map<String, CodeTypes>);
return codesQuery.getResultList();
}
}
//some code is omitted
My assumption was with this in place i got a working caching mechanism. Unfortunately it is not the case.
The spring implementation of Spring assumes that there is at least a key. When there is no argument available this becomes the String value “0″. In my application I had multiple functions that collected data from a DB but are without a specific key. (And yes I could have done this differently, for example with @PostConstruct annotation, but that is besides the point).
Bot how to solve this? The documentation does not describe that it does this.
The answer is right there in the documentation: Spring Expression language (SpEL).
By means of the SpEL you can use for example the method name to use as a key of the cached value.
With this the annotated method becomes:
@Component
public class MyDataImpl implements MyData {
@PersistenceContext(unitName = "persistenceUnit")
private EntityManager entityManager;
@Cacheable(value = SpringConfiguration.CORE_CACHE,
key = "#root.method.name")
@Override public Map<String, Code> getCodes() {
final TypedQuery<Map<String, CodeTypes>>
codesQuery =
entityManager.createNamedQuery(CODE_QUERY,
Map<String, CodeTypes>);
return codesQuery.getResultList();
}
}
With this annotation the cached element is stored in the map with the method name as the key.