@Caching With Multiple Keys

47,273

Solution 1

Yes, you can specify using a Spring-EL expression along these lines:

@Override
@Cacheable(key="#bar.name.concat('-').concat(#bar.id)")
public int foo(Bar bar) {
    ....
}

or define a modified hashCode on bar and call that:

@Override
@Cacheable(key="#bar.hashCodeWithIdName")
public int foo(Bar bar) {
    ....
}

Solution 2

You can use this approach also

@Override
@Cacheable(key="{#bar.name, #bar.id}")
public int foo(Bar bar) {
    ....
}

It is suggested not to use hashcode as keys @Cacheable key on multiple method arguments

Solution 3

You can use Spring SimpleKey class

@Cacheable(value = "barCache", key = "new org.springframework.cache.interceptor.SimpleKey(#bar.id, #bar.name)")

Solution 4

Both answers by @Biju and @vsingh are correct; but I would like to add one more alternative if the Bar object you are trying to cache is complex or the foo method contains a large amount of parameters using SpEL might not be the most ideal solution for generating the key.

Alternatively you may want to consider keyGenerator.

Example:

@Override
@Cacheable(value="barCahceKey", keyGenerator="barKeyGenerator")
public int foo(Bar bar) {
  ....
}

@Component
public class BarKeyGenerator implements KeyGenerator {
@Override
public Object generate(Object o, Method method, Object... objects) {
      // TODO logic to generate unique key
      return "Bar_Key_Generator_With_Params_etc";
    }
}

With this approach you have the fully flexibility of how the key is constructed.

KeyGenerator API

Share:
47,273
Alex Beardsley
Author by

Alex Beardsley

Updated on July 31, 2022

Comments

  • Alex Beardsley
    Alex Beardsley almost 2 years

    I have a service that takes in a DTO and returns some result:

    @Override
    public int foo(Bar bar) {
        ....
    }
    

    Bar is as follows (simplified):

    public class Bar {
        public int id;
        public String name;
        public String baz;
    
        @Override
        public int hashCode() {
            //this is already being defined for something else
            ...
        }
    
        @Override
        public boolean equals(Object o) {
            //this is already being defined for something else
            ...
        }
    }
    

    I want to use @Cacheable on the foo method; however, I want to hash on the id and name properties, but not baz. Is there a way to do this?

  • NBJack
    NBJack about 3 years
    Hash codes are dangerous as they aren't guaranteed to be unique.
  • Sanjay
    Sanjay almost 3 years
    But when I use this approach it's throwing java.lang.ClassCastException: Invalid key type, expected : org.springframework.cache.interceptor.SimpleKey but was : java.util.ArrayList. How can I get over this?
  • Sanjay
    Sanjay almost 3 years
    This works for but why does @vsingh solution gives me java.lang.ClassCastException: Invalid key type, expected : org.springframework.cache.interceptor.SimpleKey but was : java.util.ArrayList exception?
  • Sanjay
    Sanjay almost 3 years
    But why does it require key = "new org.springframework.cache.interceptor.SimpleKey(#bar.id, #bar.name)" instead of key = "{#bar.id, #bar.name}"
  • study_20160808
    study_20160808 almost 3 years
    If '-' is public static final String data. How should I do?
  • Akanksha gore
    Akanksha gore over 2 years
    Custom keyGenerator allow to create only 1 key. What about having multipe keys from KeyGenerator? This KeyGenerator only return Object not array of object. So, I guess custom KeyGenerator cannot be used for multiple key generation.