Caching

Intermediate
Data & JPA

Spring's caching abstraction (@Cacheable, @CacheEvict, @CachePut) decouples cache logic from business logic. Back it with Redis for a distributed cache shared across all app instances.

Overview

Spring Cache wraps method results in a cache — on the first call the real method executes and the result is stored; on subsequent calls with the same arguments the cached value is returned without executing the method. @Cacheable is for reads, @CacheEvict is for deletes/updates, @CachePut always executes the method and updates the cache. The cache name maps to a specific store (e.g. a Redis key prefix). With Redis as the backend, the cache is shared across all pods.

@Cacheable, @CacheEvict, @CachePut

Enable caching with @EnableCaching on a @Configuration class. Cache names are logical identifiers — they map to cache regions in the CacheManager. The cache key defaults to the method arguments; use key = "#id" with SpEL for clarity.

Java — @Cacheable, @CachePut, @CacheEvict
@Configuration
@EnableCaching
public class CacheConfig { }

@Service
public class CourseService {

    // Cache result — key = courseId
    // courses::123 stored in Redis
    @Cacheable(value = "courses", key = "#courseId")
    public CourseDto getCourse(String courseId) {
        return courseRepository.findById(courseId)
            .map(courseMapper::toDto)
            .orElseThrow(() -> new CourseNotFoundException(courseId));
        // Only called on first request — cached result returned on subsequent calls
    }

    // Condition: only cache if course is published
    @Cacheable(value = "courses", key = "#courseId",
               condition = "#result.status == 'PUBLISHED'")
    public CourseDto getCoursePublished(String courseId) { ... }

    // Always execute method AND update cache — for write operations
    @CachePut(value = "courses", key = "#result.id")
    public CourseDto updateCourse(String courseId, UpdateCourseRequest req) {
        Course updated = courseRepository.save(/* ... */);
        return courseMapper.toDto(updated);
    }

    // Remove specific entry on update/delete
    @CacheEvict(value = "courses", key = "#courseId")
    public void deleteCourse(String courseId) {
        courseRepository.deleteById(courseId);
    }

    // Evict ALL entries in the cache
    @CacheEvict(value = "courses", allEntries = true)
    @Scheduled(cron = "0 0 3 * * *")  // 3 AM daily cache refresh
    public void clearCourseCache() { }
}

Redis CacheManager with TTL

Configure RedisCacheManager to use Redis as the cache backend. Set per-cache TTL to prevent stale data from living forever. Use JSON serialization so cached values are human-readable in Redis.

Java + YAML — Redis CacheManager with per-cache TTL
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

# application.yml
spring:
  data:
    redis:
      host: ${REDIS_HOST:localhost}
      port: 6379
      password: ${REDIS_PASSWORD:}

@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public RedisCacheConfiguration defaultCacheConfig() {
        return RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(30))               // global default TTL
            .serializeValuesWith(RedisSerializationContext
                .SerializationPair
                .fromSerializer(new GenericJackson2JsonRedisSerializer())); // JSON
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
        Map<String, RedisCacheConfiguration> perCacheConfig = Map.of(
            "courses",  defaultCacheConfig().entryTtl(Duration.ofHours(1)),
            "users",    defaultCacheConfig().entryTtl(Duration.ofMinutes(5)),
            "dsa",      defaultCacheConfig().entryTtl(Duration.ofHours(24))
        );

        return RedisCacheManager.builder(factory)
            .cacheDefaults(defaultCacheConfig())
            .withInitialCacheConfigurations(perCacheConfig)
            .build();
    }
}

// Redis key pattern: {cacheName}::{key}
// e.g. courses::abc-123  →  CourseDto JSON  (expires in 1 hour)

Key Points to Remember

  • 1@EnableCaching activates Spring's caching proxy — without it, @Cacheable annotations are ignored.
  • 2@Cacheable returns the cached value on cache hit; executes the method and caches the result on miss.
  • 3@CachePut always executes the method and always updates the cache — use after write operations.
  • 4@CacheEvict removes entries — use allEntries = true sparingly (removes all keys in the cache region).
  • 5Use GenericJackson2JsonRedisSerializer for JSON-serialized Redis values — readable and debuggable.
  • 6Always set a TTL on every cache to prevent stale data accumulating in Redis forever.

Interview Questions

Sign in to ask Aria
1

What is the difference between @Cacheable and @CachePut?

EasyAmazon
2

How does Spring Cache know whether to return the cached value or call the method?

EasyWipro
3

How would you configure different TTLs for different caches in Redis?

MediumRazorpay
4

What are the risks of caching without a TTL?

EasyThoughtWorks
5

How do you handle cache inconsistency in a multi-pod deployment?

HardNetflix

Ask Aria about Caching

Your personal AI tutor — ask anything about this concept

Revision Status

Personal Notes

Sign in to save personal notes for this topic.

Discussion

Sign in to join the discussion.

Loading discussion…