Mastering SpringBoot Cache Annotations: @Cacheable and @CacheEvict

This article explains why and how to use SpringBoot's built‑in cache annotations—@Cacheable, @CacheEvict, @CachePut and @Caching—covering environment setup, annotation attributes, practical code examples, common pitfalls, and a concise comparison to help developers simplify caching logic and keep data consistent.

Java Tech Workshop
Java Tech Workshop
Java Tech Workshop
Mastering SpringBoot Cache Annotations: @Cacheable and @CacheEvict

Why use cache annotations

Non‑intrusive: business logic and cache logic are separated.

Minimal code: a single annotation replaces explicit get / set calls.

Centralized configuration: cache name, TTL and key rules are defined in application.yml.

Supports multiple providers such as Redis, Caffeine and in‑memory caches.

Basic environment preparation

1. Add dependency

<!-- Redis + cache -->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2. Enable caching in the main class

@SpringBootApplication
@EnableCaching
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3. Configure cache in application.yml

spring:
  cache:
    type: redis
  redis:
    time-to-live: 3600000   # default 1 hour
    cache-null-values: false # do not cache null values

Core annotation @Cacheable (query + cache)

Purpose : first call executes the method, stores the result in cache; subsequent calls retrieve the result from cache without executing the method body.

1. Basic usage

@Cacheable(value = "userCache", key = "#userId")
public User getUserById(Long userId) {
    return userMapper.selectById(userId);
}
value / cacheNames

: cache name that distinguishes business domains. key: cache key, supports SpEL expressions.

Resulting Redis key example:

userCache::1001

2. Common key patterns

// single parameter
@Cacheable(value = "user", key = "#id")

// object parameter, use its id
@Cacheable(value = "user", key = "#user.id")

// method name as key
@Cacheable(value = "dict", key = "#root.methodName")

// composite key
@Cacheable(value = "order", key = "'uid:' + #userId + ':type:' + #type")

3. Conditional caching

// cache only adult users
@Cacheable(value = "user", key = "#userId", condition = "#result.age >= 18")

// cache only when result is not null
@Cacheable(value = "user", key = "#userId", unless = "#result == null")

Core annotation @CacheEvict (delete cache)

Purpose : after data update or deletion, clear stale cache to keep data consistent.

1. Delete by key

@CacheEvict(value = "userCache", key = "#user.id")
public void updateUser(User user) {
    userMapper.updateById(user);
}

2. Delete all entries under a cache name

@CacheEvict(value = "userCache", allEntries = true)
public void refreshAllUser() { }

3. Delete before method execution

@CacheEvict(value = "user", key = "#userId", beforeInvocation = true)

Additional common annotations

1. @CachePut

Forces cache update; the method body is always executed, suitable for real‑time synchronization.

@CachePut(value = "user", key = "#user.id")
public User updateUser(User user) {
    userMapper.updateById(user);
    return user;
}

2. @Caching

Combines multiple cache operations.

@Caching(
    evict = {
        @CacheEvict(value = "user", key = "#userId"),
        @CacheEvict(value = "userOrder", key = "#userId")
    }
)
public void deleteUser(Long userId) { }

Simulated scenarios

Scenario 1 – User detail (typical query cache)

@Cacheable(value = "userInfo", key = "#userId", unless = "#result == null")
public User getUser(Long userId) {
    return userMapper.selectById(userId);
}

Scenario 2 – Update user → clear cache

@CacheEvict(value = "userInfo", key = "#user.id")
public void updateUser(User user) {
    userMapper.updateById(user);
}

Scenario 3 – Delete user → clear cache

@CacheEvict(value = "userInfo", key = "#userId")
public void deleteUser(Long userId) {
    userMapper.deleteById(userId);
}

Scenario 4 – Product list cache

@Cacheable(value = "productList", key = "#categoryId")
public List<Product> getProductList(Integer categoryId) {
    return productMapper.selectByCategory(categoryId);
}

Scenario 5 – Bulk refresh product cache

@CacheEvict(value = "productList", allEntries = true)
public void refreshProduct() { }

Scenario 6 – Dictionary/config cache (rarely changes)

@Cacheable(value = "dictCache", key = "#dictType")
public List<Dict> getDict(String dictType) {
    return dictMapper.selectByType(dictType);
}

Precautions

Self‑invocation disables annotations : internal method calls bypass AOP proxies. Resolve by extracting the method to a separate service or injecting the bean into itself.

Cache key collisions : use distinct value/cacheNames for different business domains.

Cache‑DB inconsistency : pair write operations with @CacheEvict or @CachePut to keep cache synchronized.

Large list caching : caching huge lists can consume memory; apply shorter TTLs and consider pagination.

Null values being cached : prevent by adding unless = "#result == null" or setting cache-null-values: false in configuration.

Transaction and cache order : clear the cache after the transaction commits to avoid dirty cache.

Comparison of the three main cache annotations

@Cacheable

: reads and caches; method executes only on cache miss; typical for query interfaces. @CachePut: forces cache update; method always executes; suitable for real‑time synchronization. @CacheEvict: removes cache entries; method always executes; used for create, update or delete operations.

Summary

Use @Cacheable for read‑heavy, write‑light scenarios such as user details, dictionary data or product lists.

Use @CacheEvict for updates and deletions to purge stale entries.

Use @CachePut when the cache must be refreshed on every write.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

RedisCachingSpringBootAnnotationCacheableCacheEvictCachePut
Java Tech Workshop
Written by

Java Tech Workshop

Focused on Java backend technologies, sharing fundamentals, multithreading, JVM, the Spring ecosystem, microservices, distributed systems, high concurrency, source‑code analysis, and practical experience. Continuously delivers high‑quality original content, interview guides, and learning roadmaps to help Java developers progress from beginner to advanced, enhancing technical skills and core competitiveness.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.