Spring Data Redis
SkillDatabases & dataUse when implementing caching, session storage, rate limiting, or any Redis integration. Covers cache-aside pattern, key naming, TTL strategy, and serialization config.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Spring Data Redis skill
What this skill tells your AI
The instructions your AI receives, as published by rrezartprebreza/spring-boot-skills in skills/spring-boot-3/spring-data-redis/SKILL.md and read by ahel’s review.
Dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Configuration
@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); // JSON, not Java serialize
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10))
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.withCacheConfiguration("orders", config.entryTtl(Duration.ofMinutes(5)))
.withCacheConfiguration("products", config.entryTtl(Duration.ofHours(1)))
.build();
}
}
Key Naming Convention
{app}:{domain}:{id} → orders:order:uuid-here
{app}:{domain}:list:{filter} → orders:order:list:status:PENDING
{app}:session:{userId} → orders:session:uuid-here
{app}:ratelimit:{ip} → orders:ratelimit:192.168.1.1
@Cacheable — Declarative Caching
@Service
@RequiredArgsConstructor
public class ProductService {
@Cacheable(value = "products", key = "#id")
public ProductResponse findById(UUID id) {
return productRepository.findById(id)
.map(ProductResponse::from)
.orElseThrow(() -> new EntityNotFoundException("Product not found: " + id));
}
@CachePut(value = "products", key = "#result.id") // update cache after write
@Transactional
public ProductResponse update(UUID id, UpdateProductRequest request) {
Product product = productRepository.findById(id).orElseThrow();
product.update(request);
return ProductResponse.from(productRepository.save(product));
}
@CacheEvict(value = "products", key = "#id") // invalidate on delete
@Transactional
public void delete(UUID id) {
productRepository.deleteById(id);
}
@CacheEvict(value = "products", allEntries = true) // clear all
public void clearCache() {}
}
Manual Cache-Aside Pattern
@Service
@RequiredArgsConstructor
public class OrderCacheService {
private final RedisTemplate<String, Object> redisTemplate;
private final ObjectMapper objectMapper;
private static final Duration TTL = Duration.ofMinutes(5);
public Optional<OrderResponse> get(UUID orderId) {
String key = "orders:order:" + orderId;
Object cached = redisTemplate.opsForValue().get(key);
if (cached == null) return Optional.empty();
return Optional.of(objectMapper.convertValue(cached, OrderResponse.class));
}
public void put(OrderResponse order) {
String key = "orders:order:" + order.id();
redisTemplate.opsForValue().set(key, order, TTL);
}
public void evict(UUID orderId) {
redisTemplate.delete("orders:order:" + orderId);
}
}
Rate Limiting with Redis
@Component
@RequiredArgsConstructor
public class RateLimiter {
private final RedisTemplate<String, String> redisTemplate;
public boolean isAllowed(String identifier, int maxRequests, Duration window) {
String key = "ratelimit:" + identifier;
Long count = redisTemplate.opsForValue().increment(key);
if (count == 1) {
redisTemplate.expire(key, window);
}
return count <= maxRequests;
}
}
application.yml
spring:
data:
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
timeout: 2000ms
lettuce:
pool:
max-active: 10
max-idle: 5
min-idle: 2
cache:
type: redis
Cache Stampede
When a hot key expires, every concurrent request misses at once and they all hammer the DB to recompute the same value (the "thundering herd"). For expensive, high-traffic loads, let one caller compute while the rest wait:
// sync = true — only one thread computes the value; others block on it
@Cacheable(value = "products", key = "#id", sync = true)
public ProductResponse findById(UUID id) { ... }
sync = true serializes recomputation per key within a single instance. For a fleet-wide guarantee,
add a short Redis lock (SETNX with a TTL) around the recompute. Pair with jittered TTLs so a batch of
keys written together doesn't all expire on the same second.
Gotchas
- Agent uses Java serialization for values — always use JSON (
GenericJackson2JsonRedisSerializer) - Agent caches entities with JPA lazy fields — cache DTOs/response objects, not entities
- Agent uses no TTL — always set expiry, memory is not infinite
- Agent forgets
@EnableCaching—@Cacheablesilently does nothing without it - Agent caches
nullvalues — use.disableCachingNullValues()to avoid storing misses - Agent leaves hot keys unprotected — use
@Cacheable(sync = true)to prevent stampede on expiry - Agent gives every entry the same TTL — add jitter so keys don't expire in a synchronized wave
Signals
- GitHub stars
- 263
- Forks
- 40
- Last commit
- Sep 2026
ahel review
S4info
community integration — published by rrezartprebreza, not redis
Automated review, not a security audit. Ruleset v1.
Advanced
- Catalog kind
- skill
- Gateway key
spring-data-redis- Source
- github.com/rrezartprebreza/spring-boot-skills