• Home
  • Custom Redis Caching in Spring Boot Using Jedis

As a Java professional, when designing applications that demand high performance and responsiveness, implementing caching can significantly improve efficiency. Redis is a powerful in-memory key-value store, and Spring Boot seamlessly integrates with it. While the Spring Cache abstraction (@Cacheable) is convenient, there are cases where full control is essential. In such cases, manually interacting with Redis using RedisTemplate offers the precision needed.

This article explores how to build a custom Redis caching layer in a Spring Boot application using the Jedis client. We will also cover how Redis templates work, when to use multiple templates, and how Redis keys are managed.


Why Manual Redis Cache Management?

While annotations like @Cacheable offer simplicity, manual cache handling gives you:

  • Full control over key structure
  • Fine-grained TTL (time-to-live) management
  • Custom serialization
  • Domain-specific cache logic

Redis Configuration with Jedis and RedisTemplate

@Configuration
public class RedisConfig {

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        return new JedisConnectionFactory();
    }

    @Bean
    public RedisTemplate<String, User> userRedisTemplate() {
        RedisTemplate<String, User> template = new RedisTemplate<>();
        template.setConnectionFactory(jedisConnectionFactory());
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new Jackson2JsonRedisSerializer<>(User.class));
        return template;
    }
}

The RedisTemplate<String, User> allows interaction with Redis using String keys and User object values. We use Jackson for JSON serialization.


RedisTemplate and opsForValue()

The method redisTemplate.opsForValue() returns ValueOperations<K, V> which provides operations for handling simple key-value pairs:

redisTemplate.opsForValue().set("user:123", user);      // Save
User user = redisTemplate.opsForValue().get("user:123"); // Retrieve

You can also set TTL:

redisTemplate.opsForValue().set("user:123", user, 10, TimeUnit.MINUTES);

Custom User Cache Service

@Service
public class UserCacheService {

    private static final String PREFIX = "user:";

    @Autowired
    private RedisTemplate<String, User> userRedisTemplate;

    public void saveUserToCache(User user) {
        userRedisTemplate.opsForValue().set(PREFIX + user.getId(), user);
    }

    public User getUserFromCache(String id) {
        return userRedisTemplate.opsForValue().get(PREFIX + id);
    }

    public void deleteUserFromCache(String id) {
        userRedisTemplate.delete(PREFIX + id);
    }

    public boolean existsInCache(String id) {
        return Boolean.TRUE.equals(userRedisTemplate.hasKey(PREFIX + id));
    }
}

REST Controller for Testing

@RestController
@RequestMapping("/custom-cache/users")
public class UserController {

    @Autowired
    private UserCacheService userCacheService;

    @PostMapping
    public ResponseEntity<String> cacheUser(@RequestBody User user) {
        userCacheService.saveUserToCache(user);
        return ResponseEntity.ok("User cached");
    }

    @GetMapping("/{id}")
    public ResponseEntity<User> getCachedUser(@PathVariable String id) {
        User user = userCacheService.getUserFromCache(id);
        return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<String> removeCachedUser(@PathVariable String id) {
        userCacheService.deleteUserFromCache(id);
        return ResponseEntity.ok("User cache entry deleted");
    }

    @GetMapping("/{id}/exists")
    public ResponseEntity<Boolean> checkCache(@PathVariable String id) {
        return ResponseEntity.ok(userCacheService.existsInCache(id));
    }
}

Key Management: Should You Use Multiple Templates?

You do not need separate RedisTemplate instances for each domain object (e.g., User, Company, Role) if you use a universal serializer like GenericJackson2JsonRedisSerializer. However, if you want type safety, strict serialization, or domain-specific settings, separate templates can be useful.

Best Practice

  • Use key prefixes like user:123, company:123 to avoid key collisions.
  • Spring Cache abstraction already prefixes keys using cache names (users::123 vs companies::123).
  • Only define multiple templates if different serializers or behaviors are needed.

Summary

Using Redis manually in Spring Boot gives you powerful control for caching domain data. RedisTemplate and opsForValue() let you perform raw Redis operations such as set, get, and delete, while preserving type safety and structure with JSON serialization. Whether you want full CRUD on cache or domain-specific logic, this approach offers flexibility beyond the limits of @Cacheable.

Credits: Babar Shahzad

Leave Comment