java 43 lines · 7 steps

How Spring cache annotations keep data fresh

A Spring service uses @Cacheable and @CacheEvict to serve reads from cache while invalidating stale entries on writes.

Explained by highlit
1@Service
2public class ProductCatalogService {
3 
4 private final ProductRepository productRepository;
5 private final PricingClient pricingClient;
6 
7 public ProductCatalogService(ProductRepository productRepository, PricingClient pricingClient) {
8 this.productRepository = productRepository;
9 this.pricingClient = pricingClient;
10 }
11 
12 @Cacheable(cacheNames = "products", key = "#productId")
13 public ProductView getProduct(long productId) {
14 Product product = productRepository.findById(productId)
15 .orElseThrow(() -> new ProductNotFoundException(productId));
16 Money price = pricingClient.resolveCurrentPrice(product.getSku());
17 return ProductView.from(product, price);
18 }
19 
20 @Cacheable(cacheNames = "productsByCategory", key = "#category", unless = "#result.isEmpty()")
21 public List<ProductView> getProductsByCategory(String category) {
22 return productRepository.findByCategory(category).stream()
23 .map(p -> ProductView.from(p, pricingClient.resolveCurrentPrice(p.getSku())))
24 .toList();
25 }
26 
27 @Caching(evict = {
28 @CacheEvict(cacheNames = "products", key = "#result.id"),
29 @CacheEvict(cacheNames = "productsByCategory", key = "#command.category")
30 })
31 @Transactional
32 public Product updateProduct(UpdateProductCommand command) {
33 Product product = productRepository.findById(command.productId())
34 .orElseThrow(() -> new ProductNotFoundException(command.productId()));
35 product.rename(command.name());
36 product.recategorize(command.category());
37 return productRepository.save(product);
38 }
39 
40 @CacheEvict(cacheNames = {"products", "productsByCategory"}, allEntries = true)
41 public void invalidateAll() {
42 }
43}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Cache reads with @Cacheable and evict on writes so callers never see stale product data.
  2. 2SpEL expressions like key and unless let you shape cache keys and skip caching empty results.
  3. 3Grouping multiple @CacheEvict under @Caching keeps two related caches consistent from a single write.

Related explainers

Share this explainer

Here's the card — post it anywhere.

How Spring cache annotations keep data fresh — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code