Spring Boot Caching

Caching stores the result of an expensive operation so your app returns it instantly on the next call instead of repeating the work. Spring Boot's caching layer lets you add caching with one annotation — no manual cache management needed.

Why Caching Matters

Without cache:                     With cache:
──────────────────────────────     ──────────────────────────────────
Request 1: query DB (50ms)         Request 1: query DB (50ms) → store
Request 2: query DB (50ms)         Request 2: return from cache (1ms)
Request 3: query DB (50ms)         Request 3: return from cache (1ms)
Request 100: query DB (50ms)       Request 100: return from cache (1ms)

100 requests = 5000ms total        100 requests ≈ 149ms total

Caching is especially useful for data that changes infrequently — product catalogs, country lists, user profiles.

Enable Caching

@SpringBootApplication
@EnableCaching                     ← Activates Spring's caching infrastructure
public class DemoApplication { ... }

The Three Core Annotations

Annotation          When It Runs              What It Does
──────────────────  ────────────────────────  ────────────────────────────────
@Cacheable          Before method runs        Return cached value if it exists;
                                              run method and cache result if not
@CachePut           Always runs method        Always runs and updates the cache
@CacheEvict         Always runs method        Removes value(s) from the cache

@Cacheable — The Most Common

@Service
public class ProductService {

    @Cacheable(value = "products", key = "#id")
    public Product findById(Long id) {
        // This code runs only on the FIRST call for each id
        // Subsequent calls return the cached result
        return productRepo.findById(id).orElseThrow();
    }
}

What happens on each call:

Call 1: findById(5)
    → Cache miss (nothing stored for key "5")
    → Runs method → queries DB → returns Product
    → Stores Product in cache under key "5"

Call 2: findById(5)
    → Cache hit (Product stored under key "5")
    → Returns Product immediately — no DB query

Call 3: findById(7)
    → Cache miss (nothing stored for key "7")
    → Runs method → queries DB → returns Product
    → Stores Product in cache under key "7"

@CachePut — Update the Cache

@CachePut(value = "products", key = "#product.id")
public Product updateProduct(Product product) {
    Product saved = productRepo.save(product);
    return saved;    ← Cache is updated with the new value
}

@CacheEvict — Remove from Cache

// Remove one entry
@CacheEvict(value = "products", key = "#id")
public void deleteProduct(Long id) {
    productRepo.deleteById(id);
}

// Clear the entire cache
@CacheEvict(value = "products", allEntries = true)
public void clearProductCache() { }

Default Cache (ConcurrentHashMap)

Out of the box, Spring uses an in-memory ConcurrentHashMap as the cache store. This works for a single server. For multiple servers, use a distributed cache like Redis.

Using Redis as a Cache

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
# application.properties
spring.cache.type=redis
spring.data.redis.host=localhost
spring.data.redis.port=6379

# Cache entries expire after 10 minutes
spring.cache.redis.time-to-live=600000

Caching with Conditions

// Only cache if the result is not null
@Cacheable(value = "products", key = "#id", unless = "#result == null")
public Product findById(Long id) { ... }

// Only cache for active products
@Cacheable(value = "products", key = "#id", condition = "#id > 0")
public Product findById(Long id) { ... }

Cache Flow Diagram

  Request: findById(5)
        │
        ▼
  Cache: check key "5"
        │
        ├── HIT → return Product immediately (no DB)
        │
        └── MISS → run method
                     │
                     ▼
               Query Database
                     │
                     ▼
               Store in cache
                     │
                     ▼
               Return Product

Summary

  • Enable caching with @EnableCaching on the main class
  • @Cacheable returns cached data on repeat calls without hitting the DB
  • @CachePut always runs the method and refreshes the cache
  • @CacheEvict removes stale entries when data changes
  • Use Redis for distributed caching when running multiple server instances

Leave a Comment

Your email address will not be published. Required fields are marked *