Reputation: 21173
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?
Upvotes: 22
Views: 74343
Reputation: 80
public class MyKeyGenerator implements KeyGenerator {
public Object generate(Object target, Method method, Object... params) {
if (params.length > 2) {
// only the first two, please! this was useful in my case.
// omit or use like this, if (not) needed
params = Arrays.copyOf(params, 2);
}
var key = String.format("mykey_%s", StringUtils.arrayToDelimitedString(params, "_"));
return key;
}
}
In your CacheConfig you inject the bean like:
@Bean("myKeyGenerator") KeyGenerator myKeyGenerator() {
return new MyCacheKeyGenerator();
}
And in your repository-class like:
// assuming you also defined a "myCache" in your cache-config!
@Cacheable(value = "myCache", keyGenerator = "myKeyGenerator", unless="#result == null")
public byte[] getMyData(int objectId, int objectType, boolean furtherParams, int willBeIgnored) {
}
@CacheEvict(value = "myCache", keyGenerator = "myKeyGenerator")
public void storeMyData(int id, int type, byte[] myData, int changingUserId) {
// store here and do the same annotation for the delete-method
}
Upvotes: 0
Reputation: 3717
@Cacheable(value = "myCacheByNameUserCity", key = "T(java.util.Objects).hash(#name, #user, #city)")
public User getUser(String name, String user,String city)
I propose this method with Object.hash, because "new org.springframework.cache.interceptor.SimpleKey (# bar.id, # bar.name)" returns toString () So it returns:
getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]";
So finally it returns "SimpleKey [bla,blaa, blalala]" not a unique code
Upvotes: 0
Reputation: 560
You can use Spring SimpleKey class
@Cacheable(value = "barCache", key = "new org.springframework.cache.interceptor.SimpleKey(#bar.id, #bar.name)")
Upvotes: 2
Reputation: 27
The keys from the same Object, you can use object.hashCode(), so you don't need to specific keys one by one
@Override
@Cacheable(key="#bar.hashCode()")
public int foo(Bar bar) {
....
}
OR if you have an object and another key
@Override
@Cacheable(key="{#bar.hashCode(), #anotherKey}")
public int foo(Bar bar) {
....
}
I think this is a better solution.
Upvotes: -2
Reputation: 5425
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.
Upvotes: 2
Reputation: 6749
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
Upvotes: 61
Reputation: 49915
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) {
....
}
Upvotes: 20