Spring Boot - Redis
Published: 2024-05-04 21:59:48

### Docker Compose File

Here is the basic docker-compose redis configuration

```
services:
    redis:
    image: redis:latest
    restart: always
    ports:
      - "6379:6379"
    environment:
      - REDIS_PASSWORD=my-password
      - REDIS_PORT=6379
      - REDIS_DATABASES=16
```

### Maven dependency

```
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```

### Application yml file

```
spring:
  cache:
    type: redis
    redis:
      host: localhost
      port: 6379

cache:
  config:
    entryTtl: 60
    jwtToken:
      entryTtl: 30
```


In application yml file, cache type and redis host, port informations are defined.
Redis config class
```
@Configuration
@EnableCaching
@Slf4j
public class RedisConfig {

    @Value("${spring.cache.redis.host}")
    private String redisHost;

    @Value("${spring.cache.redis.port}")
    private int redisPort;

    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort);
        configuration.setPassword("my-password");
        log.info("Connected to : {} {}", redisHost, redisPort);

        return new LettuceConnectionFactory(configuration);
    }

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        return RedisCacheManager.create(connectionFactory);
    }
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(){
        final RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new GenericToStringSerializer<Object>(Object.class));
        redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer());
        redisTemplate.setValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory());
        return redisTemplate;
    }
}
```

## Working with cache annotations

To enable caching in your application, you can use the `@EnableCaching` annotation and then annotate methods that should be cached with `@Cacheable`, `@CachePut`, and `@CacheEvict`

```
@GetMapping("/product/{id}")  
@Cacheable(value = "product", key = "#id")
public Product getProductById(@PathVariable long id) {...}
```

**1.**  **@Cacheable** is employed to fetch data from the database, storing it in the cache. Upon future invocations, the method retrieves the cached value directly, eliminating the need to execute the method again.

The `value` attribute establishes a cache with a specific name, while the `key` attribute permits the use of Spring Expression Language to compute the key dynamically. Consequently, the method result is stored in the ‘product’ cache, where respective ‘product_id’ serves as the unique key. This approach optimizes caching by associating each result with a distinct key.

**2.**  **@CachePut** is used to update data in the cache when there is any update in the source database.

```
@PutMapping("/product/{id}")
@CachePut(cacheNames = "product", key = "#id")
public Product editProduct(@PathVariable long id, @RequestBody Product product) {...}
```

**3**. **@CacheEvict** is used for removing stale or unused data from the cache.

```
@DeleteMapping("/product/{id}")
@CacheEvict(cacheNames = "product", key = "#id", beforeInvocation = true)
public String removeProductById(@PathVariable long id) {...}
```

We use cacheName and key to remove specific data from the cache. The beforeInvocation attribute allows us to control the eviction process, enabling us to choose whether the eviction should occur before or after the method execution.

```
@DeleteMapping("/product/{id}")
@CacheEvict(cacheNames = "product", allEntries = true)
public String removeProductById(@PathVariable long id) {...}
```

Alternatively, all the data can also be removed for a given cache by using the allEntries attribute as true. The annotation allows us to clear data for multiple caches as well by providing multiple values as cacheName.


**4**. **@Caching** is used for multiple nested caching on the same method.

```
@PutMapping("/{id}")
@Caching(
     evict = {@CacheEvict(value = "productList", allEntries = true)},
     put = {@CachePut(value = "product", key = "#id")}
)
public Product editProduct(@PathVariable long id, @RequestBody Product product) {...}
```

As Java doesn’t allow multiple annotations of the same type to be declared for a method, doing so will generate a compilation error. We can group different annotations into Caching, as shown.

**5**. **@CacheConfig** is used for centralized configuration.

```
@CacheConfig(cacheNames = "product")
public class ProductController {...}
```

## Working with RedisTemplate


You can use the RedisTemplate to interact with Redis. For example, to save data in Redis:

```
@Autowired
private RedisTemplate<String, Object> redisTemplate;

public void saveData(String key, Object data) {
    redisTemplate.opsForValue().set(key, data);
}
```

Retrieve data from Redis

```
public Object getData(String key) {
    return redisTemplate.opsForValue().get(key);
}
```

Example service class for redis template

```
@Service
public class CacheTokenService {

    private final StringRedisTemplate stringRedisTemplate;
    private final RedisTemplate<String, Object> redisTemplate;

    public CacheTokenService(StringRedisTemplate stringRedisTemplate, RedisTemplate<String, Object> redisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.redisTemplate = redisTemplate;
    }

    public void saveData(String key, Object data) {
        redisTemplate.opsForValue().set(key, data);
    }

    public Object getData(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    public void cacheToken(String username, String token) {
        stringRedisTemplate
        .opsForValue()
        .set("token:" + username, token, Duration.ofMinutes(60));
    }

    public String getCachedToken(String username) {
        // Retrieve the token from Redis cache
        return stringRedisTemplate
        .opsForValue()
        .get("token:" + username);
    }

}
```

## Redis GUI : RedisInsight

Download link : https://redis.io/insight/