Using Spring @Cacheable annotations

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.