14

Given I have Spring Data repository and I put Cacheable annotation on findAll method:

@Repository
@CacheConfig(cacheNames = TEMPLATE_CACHE)
public interface TemplateRepository extends JpaRepository<Template, Long> {
    @Override
    @Cacheable
    List<Template> findAll();
}

Intellij IDEA show warning:

Spring Team recommends that you only annotate concrete classes (and methods of concrete classes) with the @Cache* annotation, as opposed to annotating interfaces. 
You certainly can place the @Cache* annotation on an interface (or an interface method), but this works only as you would expect it to if you are using interface-based proxies. 
The fact that Java annotations are not inherited from interfaces means that if you are using class-based proxies (proxy-target-class="true") or the weaving-based aspect (mode="aspectj"),
then the caching settings are not recognized by the proxying and weaving infrastructure, and the object will not be wrapped in a caching proxy, which would be decidedly bad.

Is it really that bad?

What could happen from practical point of view?

Where should I put that annotation given I want to call findAll in e.g. Controller mapping?

How does Spring Data handle this? It seems to be working fine for me.

Patrik Mihalčin
  • 3,341
  • 7
  • 33
  • 68
  • Worst case? You dont have caching. But as far as i know, as long no one creates an implementation of the interface and removes @Repository - the spring framework will choose the correct way of proxying. I am not sure about this, so this is just a comment, not an answer. – Martin Baumgartner Feb 11 '19 at 12:36
  • 1
    It is a pretty standard project set-up to have a Service layer. Controller > Service > Repository i.e. Controller never calls repositories directly so in that case you could cache at the service layer. – Alan Hay Feb 11 '19 at 16:26
  • I employ https://en.wikipedia.org/wiki/Domain-driven_design in my project and term `Service` refers to something else than typical Controller > Service > Repository (what is more procedural than OO). So if I want to expose UI over aggregate class, there is no strong need for middle tier. – Patrik Mihalčin Feb 11 '19 at 16:37
  • 1
    It is a recommendation, not a hard rule. By default, Spring Data uses Interface-based proxies. – John Blum Feb 12 '19 at 16:41
  • 1
    Additionally, a good Service tier encapsulates business logic/rules (+ops) that should neither be in the Controller nor Repo. It is also the logical layer to setup a transactional context, apply (fine-grained) Security, or combine (remote) ops in a logical unit of work. Repos are often specific to a particular `DataSource`, or even structure within. Given these concerns, caching makes more sense at the Service tier than on the Repo, IMO. I'd rather prefer the caching abilities of the underlying provider at the DAO level. – John Blum Feb 12 '19 at 17:16

1 Answers1

1

As others have stated here, it's best to have a layer that you use to get data from your repository.

Under that service layer, you will add your cache annotations. When you do things in a somewhat unexpected way, you will find that Spring's proxies do not function as intended.

The Cacheable annotation (to the best of my knowledge) relies on weaving to function. Your call to a method is intercepted via the Spring Framework to provide a cache function. If no cache hit exists, it will continue to run the method.

Here's why it's important to put the Cacheable annotation in the right spot: You can call a cached method within the same class, but the cache will not actually be called, because of weaving.

Spring can't intercept calls in the same class. Putting it on a repository interface can certainly cause issues since Spring provides the concrete implementation via whatever Spring Data package you are using, which is why they provide that warning.

A very brief explanation: https://ducmanhphan.github.io/2019-02-19-How-Spring-Caching-mechanism-works/

Detailed info from Spring themselves: https://docs.spring.io/spring-framework/docs/current/reference/html/integration.html#cache