Spring cacheEvict based on like search - spring

I am caching some object using Spring cache implementation, the underline cache is EhCache. I am trying to evict the cache based on wildcard search for the keys,the reason is the way I stored them and I only know the partial key. Hence I wanted to do something like below. I did search this forum for relevant answer but did not find any.
#CacheEvict(beforeInvocation=true, key="userId+%")
public User getUser(String userId)
{
//some implementation
}
Now if I try this I get an error for the SPEL. Also I tried to create a custom keygenerator for this, here the eviction works if the key generator returns one key, but I have a couple of keys based on my search.
#CacheEvict(beforeInvocation=true, keyGenerator="cacheKeyEvictor")
public User getUser(String userId)
{
//some implementation
}
//Custom key generator for eviction
public class cacheKeyEvictor implements KeyGenerator {
#Override
public Object generate(Object arg0, Method arg1, Object... arg2) {
//loop the cache and do a like search and return the keys
return object; //works if I send one key. Won't work for a list of keys
}
}
Any help on this is appreciated.

Related

Evict not working in Spring boot

I have a method that fetches all the data and i am caching the result of that method but i am not able to evict the result.
#Component("cacheKeyGenerator")
public class CacheKeyGenerator implements KeyGenerator {
#Override
public Object generate(Object target, Method method, Object... params) {
final List<Object> key = new ArrayList<>();
key.add(method.getDeclaringClass().getName());
return key;
}
}
CachedMethod:-
#Override
#Cacheable(value="appCache",keyGenerator="cacheKeyGenerator")
public List<Contact> showAllContacts() {
return contactRepository.findAll();
}
#Override
#CachePut(value="appCache",key="#result.id")
public Contact addData(Contact contact) {
return contactRepository.save(contact);
}
Now when ever addData is called i want the data in the cache "appCache" with the key ="cacheKeyGenerator" to be evicted.So that the data returned by the method "showAllContacts()" is accurate.Can anyone please help!
The Entire code can be found at - https://github.com/iftekharkhan09/SpringCaching
Assuming you have a known constant cache key for showAllContacts then the solution should be to simply add #CacheEvict on addData passing in the cache name and key value:
#Override
#Caching(
put = {#CachePut(value="appCache", key="#result.id")},
evict = {#CacheEvict(cacheNames="appCache", key="someConstant")}
)
public Contact addData(Contact contact) {
return contactRepository.save(contact);
}
However because you use a key generator it is a bit more involved. Now given what your key generator does, you could instead pick a value for that cache key, making sure there can't be any collisions with the values from #result.id and use that value instead of a the key generator returned one.

How to create Spring Cache KeyGenerator that allows no caching for specified key

I just want to disable cache for users that are admins. So I write a method to generate keys as below that returns null for admins. But I get
java.lang.IllegalArgumentException: Null key returned for cache
operation
exeption.
Is there any way achieve that?
//a method that generates a menu for each user
#Cacheable(cacheNames = "topmenu", keyGenerator = "uiComponentKey")
#Override
public String renderResponse() {...}
//method used by a key generator to generate cache keys.
#Override
public Object getCacheKey() {
if (user.isAdmin()) {
return null;
}
return user.getUser().getLogin() + "#" + "topmenu";
}
I guess you can achive that using conditional caching feature. Smth like this:
#Cacheable(cacheNames = "topmenu", condition="#user.isAdmin()")
#Override
public String renderResponse(User user) {...}
Note, that you're going to have to pass user object to this method in this case.

Spring Constraint Validation Context - Database Request Caching

I've written a custom Validation Annotation and a ConstraintValidator implementation, which uses a Spring Service (and executes a Database Query):
public class MyValidator implements ConstraintValidator<MyValidationAnnotation, String> {
private final MyService service;
public MyValidator(MyService service) {
this.service = service;
}
#Override
public void initialize(MyValidationAnnotation constraintAnnotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return service.exists(value);
}
}
It's used like this:
public class MyEntity {
#Valid
List<Foo> list;
}
public class Foo {
#MyValidationAnnotation
String id;
}
This works quite nice, but service.exists(value) is getting called for every item within the list, which is correct, but could/should be optimized.
Question:
When validating an instance of MyEntity, I'd like to cache the results of the service.exists(value) calls.
I don't want to use a static HashMap<String, Boolean>, because this would cache the results for the entire application lifetime.
Is it possible to access some kind of Constraint Validation Context, which only exists while this particular validation is running, so I can put there the cached results?
Or do you have some other solution?
Thanks in advance!
You can use Spring's cache support. There might be other parts in the application which needs caching and this can be reused. And the setup is very simple too. And it will keep your code neat and readable.
You can cache your service calls. You need to put annotation on your service methods and a little bit of configuration.
And for cache provider you can use Ehcache. You have many options like setting ttl and max number of elements that can be cached and eviction policy etc etc if needed.
Or you can implement your own cache provider if your needs are simple. And if it is web request, In this cache you may find ThreadLocal to be useful. you can do all caching for this running thread using threadlocal. When the request is processed you can clear the threadlocal cache.

Spring Cache Abstraction: How to Deal With java.util.Optional<T>

We have a lot of code in our code base that's similar to the following interface:
public interface SomethingService {
#Cacheable(value = "singleSomething")
Optional<Something> fetchSingle(int somethingId);
// more methods...
}
This works fine as long we're only using local caches. But as soon as we're using a distributed cache like Hazelcast, things start to break because java.util.Optional<T> is not serializable and thus cannot be cached.
With what I've come up so far to solve this problem:
Removing java.util.Optional<T> from the method definitions and instead checking for the trusty null.
Unwrapping java.util.Optional<T> before caching the actual value.
I want to avoid (1) because it would involve a lot of refactoring. And I have no idea how to accomplish (2) without implementing my own org.springframework.cache.Cache.
What other options do I have? I would prefer a generic (Spring) solution that would work with most distributed caches (Hazelcast, Infinispan, ...) but I would accept a Hazelcast-only option too.
A potential solution would be to register a serializer for the Optional type. Hazelcast has a flexibile serialization API and you can register a serializer for any type.
For more information see the following example:
https://github.com/hazelcast/hazelcast-code-samples/tree/master/serialization/stream-serializer
So something like this:
public class OptionalSerializer implements StreamSerializer<Optional> {
#Override
public void write(ObjectDataOutput out, Optional object) throws IOException {
if(object.isPresent()){
out.writeObject(object.get());
}else{
out.writeObject(null);
}
}
#Override
public Optional read(ObjectDataInput in) throws IOException {
Object result = in.readObject();
return result == null?Optional.empty():Optional.of(result);
}
#Override
public int getTypeId() {
return 0;//todo:
}
#Override
public void destroy() {
}
}
However the solution isn't perfect because this Optional thing will be part of the actual storage. So internally the Optional wrapper is also stored and this can lead to problems with e.g. queries.

play framework cache annotation

Can somebody exlain, with a sample, how works the cache annotation in play framework 2 in java
I would like to cache the result of the method with his parameters; something like this:
#Cache(userId, otherParam)
public static User getUser(int userId, String otherParam){
return a User from dataBase if it isn't in cache.
}
Maybe a tutorial is available?
Thanks for your help.
The #Cached annotation doesn't work for every method call. It only works for Actions and moreover, you can't use parameters as a cache key (it's only a static String). If you want to know how it works, look at the play.cache.CachedAction source code.
Instead, you will have to use either Cache.get(), check if result is null and then Cache.set() or the Cache.getOrElse() with a Callable with a code like :
public static User getUser(int userId, String otherParam){
return Cache.getOrElse("user-" + userId + "-" + otherParam, new Callable<User>() {
#Override
public User call() throws Exception {
return getUserFromDatabase(userId, otherParam);
}
}, DURATION);
}
Be careful when you construct your cache keys to avoid naming-collision as they are shared across the whole application.

Resources