You are experiencing performance issues with certain frequently accessed data. How can you implement caching in Spring JPA to improve performance?

Caching frequently accessed data avoids re-querying the database for the same information repeatedly. Spring's Cache abstraction, combined with a cache provider like EHCache, Caffeine, or Redis, lets you add caching declaratively to repository or service methods without hand-rolling cache logic.

Key Points: • @EnableCaching on a configuration class activates Spring's caching support. • @Cacheable on a method stores its return value in a named cache, keyed by the method's arguments, so subsequent calls with the same arguments skip the database entirely. • @CacheEvict and @CachePut keep the cache consistent when underlying data changes, evicting or refreshing entries on updates and deletes. • The choice of cache provider matters: EHCache/Caffeine work well for a single instance, while Redis is preferred for a distributed, multi-instance deployment so all nodes share the same cache. • Cache eviction policy and TTL need to be tuned so stale data doesn't linger longer than acceptable for the use case.

Example: A product catalog service that reads the same rarely-changing product details thousands of times per minute annotates its findById lookup with @Cacheable("products"), so after the first database hit, subsequent requests for that product are served from the cache instead of hitting the database again.

Code Example:

@Service
public class ProductService {

    @Cacheable("products")
    public Product getProductById(Long id) {
        return productRepository.findById(id)
                .orElseThrow(() -> new ProductNotFoundException(id));
    }

    @CacheEvict(value = "products", key = "#product.id")
    public Product updateProduct(Product product) {
        return productRepository.save(product);
    }
}

Interview Tip: A concise interview answer is:

"I'd use Spring's Cache abstraction with a provider like Redis or Caffeine, annotating the hot read method with @Cacheable so its result is stored and reused for identical arguments instead of hitting the database every time. I'd pair that with @CacheEvict on the update path so the cache doesn't serve stale data once the underlying record changes."