spring-projects/spring-data-redis

Spring Speficification default Redis key generator

trcoelho opened this issue · 2 comments

Hello.

Am using Redis database as a result to cache some data and optimize performance, the main idea is avoid repeated database calls.

Have set it to a Spring repository as follows:

@Override @Cacheable(value = "findAll", condition="#spec != null", key = "#spec") public List<User> findAll(Specification<User> spec);

However cache key cannot be built as Specification does not have a "toString()" method and cache key failed to get and put:

Could not get cache due to: Cannot convert cache key org.springframework.data.jpa.domain.SpecificationComposition$$Lambda/0x000001b042ec3638 to String; Please register a suitable Converter via 'RedisCacheConfiguration.configureKeyConverters(...)' or override 'org.springframework.data.jpa.domain.SpecificationComposition$$Lambda/0x000001b042ec3638.toString()

Couldn't built a custom key generator as Specification does not provides any method that retrieves parameters that was set to build it.

Is there any plan or any other way to achieve that? Cache a Specification as a result integrate Spring JPA Specification and Redis?
Tried to search a solution or an explanation in spring docs however no success.

Thanks in advance.

Redis cannot store objects by their Java identity as the storage happens externally by serializing the key into bytes. Also, a JPA specification has no means of being identifiable in any way since it has no key or other details attached. In your case, it is merely a lambda.

If you want to keep using a Specification as key, I suggest that you create a subinterface (or a wrapper type) that accepts a Specification and a String (key) in your application. Then you register a Converter through RedisCacheConfiguration.configureKeyConverters(…) using the key from your Specification wrapper to create a meaningful Redis key.

Let me know how this goes.

Hi @mp911de !
Thanks for your reply!

Yes, that pretty much that I did. Instead mark repository method as @Cacheable, I implemented a new layer that wraps repository calls and this layer I marked as @Cacheable.

So instead we have:

Controller -> Service -> Repository(@Cacheable)

We have:

Controller -> Service -> CacheableRepository(@Cacheable) -> Repository

That would work better as CacheableRepository has one single responsability while Repository remains with database access responsability, in my perspective. :)

Anyways, appreciate your attention as well your suggestion.

Let me know if you have any question.

Take care!