Practical Redis Integration with Java and Spring Boot

This guide demonstrates hands-on Redis usage in Java applications, covering direct client interaction via Jedis, configuration best practices, and seamless integration with Spring Boot using both Jedis and Lettuce clients.

Command-Line Interaction

Before integrating into code, verify Redis connectivity using the CLI:

redis-cli -h 192.168.100.110 -p 6379 -a 123456
> SET user:1001 '{"name":"Alice","role":"admin"}'
> GET user:1001

Standalone Jedis Setup

Add the Jedis dependency with test scope for lightweight development use:

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
    <scope>test</scope>
</dependency>

Implement a thread-safe connection pool with modern defaults:

public class RedisConnectionPool {
    private static volatile JedisPool instance;

    public static JedisPool getInstance() {
        if (instance == null) {
            synchronized (RedisConnectionPool.class) {
                if (instance == null) {
                    JedisPoolConfig config = new JedisPoolConfig();
                    config.setMaxTotal(16);
                    config.setMaxIdle(12);
                    config.setMinIdle(4);
                    config.setMinEvictableIdleTimeMillis(60_000);
                    config.setTestOnBorrow(true);
                    instance = new JedisPool(config, "192.168.100.110", 6379, 2000, "123456");
                }
            }
        }
        return instance;
    }

    public static void closeQuietly(Jedis jedis) {
        if (jedis != null) jedis.close();
    }
}

Core Redis Data Structures

  • String: Binary-safe key-value storage supporting counters, caching, and session data.
  • Hash: Efficinet object mapping (e.g., HSET user:1001 name "Bob" age "32") with field-level operations.
  • List: Insertion-ordered collections ideal for queues, timelines, or recent activity feeds.
  • Set: Unordered, unique element collections supporting union/intersection/difference operations.
  • Sorted Set (ZSet): Scored elements enabling leaderboards, priority queues, and range-based queries.

Example ZSet usage:

@Test
void populateLeaderboard() {
    try (Jedis jedis = RedisConnectionPool.getInstance().getResource()) {
        Map<String, Double> scores = Map.of(
            "player:alpha", 2450.0,
            "player:bravo", 3120.0,
            "player:charlie", 1980.0,
            "player:delta", 2760.0
        );
        jedis.zadd("leaderboard:global", scores);
        // Retrieve top 3 players
        Set<String> topPlayers = jedis.zrevrange("leaderboard:global", 0, 2);
        topPlayers.forEach(System.out::println);
    }
}

Spring Boot Integration

Spring Boot 2.0+ defaults to Lettuce — a Netty-based, thread-safe Redis client. Too opt-in explicitly:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

Configure connection pooling in application.yml:

spring:
  redis:
    host: 192.168.100.110
    port: 6379
    password: 123456
    timeout: 5000
    lettuce:
      pool:
        max-active: 16
        max-idle: 12
        min-idle: 4
        max-wait: 10000

To revert to Jedis (e.g., for legacy compatibility), exclude Lettuce and add Jedis explicitly:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>4.4.3</version>
</dependency>

Custom RedisTemplate Configuration

Define a type-safe RedisTemplate with JSON serialization:

@Configuration
public class RedisConfiguration {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);

        Jackson2JsonRedisSerializer<Object> jsonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        jsonSerializer.setObjectMapper(mapper);

        template.setValueSerializer(jsonSerializer);
        template.setHashValueSerializer(jsonSerializer);
        template.afterPropertiesSet();

        return template;
    }
}

Utility Operations Wrapper

A simplified utility class abstracting common Redis operations:

@Component
public class CacheManager {

    private final RedisTemplate<String, Object> redisTemplate;

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

    public <T> T get(String key, Class<T> targetType) {
        Object value = redisTemplate.opsForValue().get(key);
        return value != null ? convert(value, targetType) : null;
    }

    public void set(String key, Object value, long ttlSeconds) {
        redisTemplate.opsForValue().set(key, value, Duration.ofSeconds(ttlSeconds));
    }

    public void delete(String... keys) {
        if (keys.length == 1) {
            redisTemplate.delete(keys[0]);
        } else {
            redisTemplate.delete(Arrays.asList(keys));
        }
    }

    public boolean exists(String key) {
        return Boolean.TRUE.equals(redisTemplate.hasKey(key));
    }

    public long increment(String key) {
        return redisTemplate.opsForValue().increment(key);
    }

    @SuppressWarnings("unchecked")
    private <T> T convert(Object source, Class<T> targetType) {
        if (targetType.isInstance(source)) return (T) source;
        String json = new StringRedisSerializer().serialize((String) source);
        try {
            return new ObjectMapper().readValue(json, targetType);
        } catch (Exception e) {
            throw new RuntimeException("Deserialization failed for key: " + key, e);
        }
    }
}

Tags: Redis Jedis lettuce spring-boot java

Posted on Fri, 10 Jul 2026 17:22:49 +0000 by madwormer2