Backend Development 25 min read

Mastering Spring Data Redis: Installation, Caching, and Advanced Usage

This comprehensive guide walks you through installing Redis on Linux and Windows, configuring Spring Cache annotations, setting up JSON serialization with RedisTemplate, using connection pools, and implementing a flexible RedisService with controller examples, providing all the essential steps to integrate Redis into your Spring Boot applications.

macrozheng
macrozheng
macrozheng
Mastering Spring Data Redis: Installation, Caching, and Advanced Usage

Redis Installation

Spring Data Redis is the Spring framework's way to operate Redis. This article covers Redis installation, using Spring Cache with Redis, connection pool usage, and RedisTemplate operations.

Linux

We use Docker for installation.

Download Redis 5.0 Docker image:

<code>docker pull redis:5.0</code>

Start Redis container:

<code>docker run -p 6379:6379 --name redis \
    -v /mydata/redis/data:/data \
    -d redis:5.0 redis-server --appendonly yes</code>

Windows

Use the following steps for Windows.

Download Windows Redis from GitHub .

Extract to a directory.

Run

redis-server.exe redis.windows.conf

from the command line.

Spring Cache with Redis

Spring Cache Overview

When Spring Boot uses Redis as a cache, the simplest way is Spring Cache. By adding annotations such as @Cacheable , @CachePut , @CacheEvict , and @EnableCaching , you can implement caching without dealing with Redis APIs directly.

Common Annotations

@EnableCaching

Enable caching, usually placed on the main application class.

@Cacheable

Retrieves data from cache if present; otherwise executes the method and stores the result. Typical attributes:

value : cache name (required).

key : cache key, supports SpEL.

unless : condition to skip caching.

condition : condition to cache.

@CachePut

Always executes the method and stores the result in cache. Same attributes as

@Cacheable

.

@CacheEvict

Removes entries from cache. Typical attributes:

value : cache name.

key : cache key.

condition : condition to evict.

Usage Steps

Add Redis dependency in

pom.xml

:

<code><!--redis依赖配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</code>

Configure Redis connection in

application.yml

:

<code>spring:
  redis:
    host: 192.168.6.139
    database: 0
    port: 6379
    password: # optional
    timeout: 1000ms
</code>

Add

@EnableCaching

to the main class:

<code>@EnableCaching
@SpringBootApplication
public class MallTinyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MallTinyApplication.class, args);
    }
}
</code>

Use cache annotations in service methods, e.g.,

@Cacheable

on brand detail retrieval,

@CacheEvict

on update/delete.

<code>/**
 * PmsBrandService implementation
 */
@Service
public class PmsBrandServiceImpl implements PmsBrandService {
    @Autowired
    private PmsBrandMapper brandMapper;

    @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
    public int update(Long id, PmsBrand brand) { ... }

    @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
    public int delete(Long id) { ... }

    @Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null")
    public PmsBrand getItem(Long id) { ... }
}
</code>
After testing, the cached data appeared as garbled characters and had no expiration.

Storing JSON Data

To store standard JSON with expiration, configure RedisTemplate with a JSON serializer and set a cache expiration time.

Create a configuration class (remove

@EnableCaching

from the main class):

<code>@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    public static final String REDIS_KEY_DATABASE = "mall";

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisSerializer<Object> serializer = redisSerializer();
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);
        template.afterPropertiesSet();
        return template;
    }

    @Bean
    public RedisSerializer<Object> redisSerializer() {
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(mapper);
        return serializer;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
        RedisCacheWriter writer = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer()))
                .entryTtl(Duration.ofDays(1));
        return new RedisCacheManager(writer, config);
    }
}
</code>
After testing, the cached data is now standard JSON with a 1‑day expiration.

Using Redis Connection Pool

Spring Boot 1.5 uses Jedis (non‑thread‑safe), while Spring Boot 2.x defaults to Lettuce (thread‑safe, non‑blocking). Below are steps to configure a Lettuce pool.

Jedis vs Lettuce

Jedis creates a direct connection per thread and is not thread‑safe without a pool.

Lettuce is scalable, thread‑safe, and uses Netty for non‑blocking I/O, allowing multiple threads to share a connection.

Setup Steps

Add Lettuce pool configuration in

application.yml

:

<code>spring:
  redis:
    lettuce:
      pool:
        max-active: 8 # max connections
        max-idle: 8   # max idle
        min-idle: 0   # min idle
        max-wait: -1ms # no limit
</code>

Add

commons-pool2

dependency to

pom.xml

:

<code><dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
</code>
If the dependency is missing, the application will fail with a NoClassDefFoundError .

Free‑Form Redis Operations

Spring Cache is convenient but limited. For custom expiration or intermediate values, use RedisTemplate directly.

RedisService Interface

Defines common operations for strings, hashes, sets, and lists.
<code>public interface RedisService {
    void set(String key, Object value, long time);
    void set(String key, Object value);
    Object get(String key);
    Boolean del(String key);
    Long del(List<String> keys);
    Boolean expire(String key, long time);
    Long getExpire(String key);
    Boolean hasKey(String key);
    Long incr(String key, long delta);
    Long decr(String key, long delta);
    Object hGet(String key, String hashKey);
    Boolean hSet(String key, String hashKey, Object value, long time);
    void hSet(String key, String hashKey, Object value);
    Map<Object, Object> hGetAll(String key);
    Boolean hSetAll(String key, Map<String, Object> map, long time);
    void hSetAll(String key, Map<String, Object> map);
    void hDel(String key, Object... hashKey);
    Boolean hHasKey(String key, String hashKey);
    Long hIncr(String key, String hashKey, Long delta);
    Long hDecr(String key, String hashKey, Long delta);
    Set<Object> sMembers(String key);
    Long sAdd(String key, Object... values);
    Long sAdd(String key, long time, Object... values);
    Boolean sIsMember(String key, Object value);
    Long sSize(String key);
    Long sRemove(String key, Object... values);
    List<Object> lRange(String key, long start, long end);
    Long lSize(String key);
    Object lIndex(String key, long index);
    Long lPush(String key, Object value);
    Long lPush(String key, Object value, long time);
    Long lPushAll(String key, Object... values);
    Long lPushAll(String key, Long time, Object... values);
    Long lRemove(String key, long count, Object value);
}
</code>

RedisServiceImpl

Implements the interface using RedisTemplate .
<code>@Service
public class RedisServiceImpl implements RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void set(String key, Object value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }
    @Override
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    @Override
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
    @Override
    public Boolean del(String key) {
        return redisTemplate.delete(key);
    }
    @Override
    public Long del(List<String> keys) {
        return redisTemplate.delete(keys);
    }
    @Override
    public Boolean expire(String key, long time) {
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }
    @Override
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }
    @Override
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }
    @Override
    public Long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }
    @Override
    public Long decr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, -delta);
    }
    @Override
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }
    @Override
    public Boolean hSet(String key, String hashKey, Object value, long time) {
        redisTemplate.opsForHash().put(key, hashKey, value);
        return expire(key, time);
    }
    @Override
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }
    @Override
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }
    @Override
    public Boolean hSetAll(String key, Map<String, Object> map, long time) {
        redisTemplate.opsForHash().putAll(key, map);
        return expire(key, time);
    }
    @Override
    public void hSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }
    @Override
    public void hDel(String key, Object... hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }
    @Override
    public Boolean hHasKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }
    @Override
    public Long hIncr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }
    @Override
    public Long hDecr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
    }
    @Override
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }
    @Override
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }
    @Override
    public Long sAdd(String key, long time, Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        expire(key, time);
        return count;
    }
    @Override
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }
    @Override
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }
    @Override
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }
    @Override
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }
    @Override
    public Long lSize(String key) {
        return redisTemplate.opsForList().size(key);
    }
    @Override
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }
    @Override
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }
    @Override
    public Long lPush(String key, Object value, long time) {
        Long idx = redisTemplate.opsForList().rightPush(key, value);
        expire(key, time);
        return idx;
    }
    @Override
    public Long lPushAll(String key, Object... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }
    @Override
    public Long lPushAll(String key, Long time, Object... values) {
        Long cnt = redisTemplate.opsForList().rightPushAll(key, values);
        expire(key, time);
        return cnt;
    }
    @Override
    public Long lRemove(String key, long count, Object value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }
}
</code>

RedisController

Provides endpoints to test the RedisService.
<code>@Api(tags = "RedisController", description = "Redis testing")
@Controller
@RequestMapping("/redis")
public class RedisController {
    @Autowired
    private RedisService redisService;
    @Autowired
    private PmsBrandService brandService;

    @ApiOperation("Test simple cache")
    @RequestMapping(value = "/simpleTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<PmsBrand> simpleTest() {
        List<PmsBrand> list = brandService.list(1, 5);
        PmsBrand brand = list.get(0);
        String key = "redis:simple:" + brand.getId();
        redisService.set(key, brand);
        PmsBrand cached = (PmsBrand) redisService.get(key);
        return CommonResult.success(cached);
    }

    @ApiOperation("Test hash cache")
    @RequestMapping(value = "/hashTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<PmsBrand> hashTest() {
        List<PmsBrand> list = brandService.list(1, 5);
        PmsBrand brand = list.get(0);
        String key = "redis:hash:" + brand.getId();
        Map<String, Object> map = BeanUtil.beanToMap(brand);
        redisService.hSetAll(key, map);
        Map<Object, Object> cached = redisService.hGetAll(key);
        PmsBrand cachedBrand = BeanUtil.mapToBean(cached, PmsBrand.class, true);
        return CommonResult.success(cachedBrand);
    }

    @ApiOperation("Test set cache")
    @RequestMapping(value = "/setTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<Set<Object>> setTest() {
        List<PmsBrand> list = brandService.list(1, 5);
        String key = "redis:set:all";
        redisService.sAdd(key, (Object[]) ArrayUtil.toArray(list, PmsBrand.class));
        redisService.sRemove(key, list.get(0));
        Set<Object> cached = redisService.sMembers(key);
        return CommonResult.success(cached);
    }

    @ApiOperation("Test list cache")
    @RequestMapping(value = "/listTest", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<Object>> listTest() {
        List<PmsBrand> list = brandService.list(1, 5);
        String key = "redis:list:all";
        redisService.lPushAll(key, (Object[]) ArrayUtil.toArray(list, PmsBrand.class));
        redisService.lRemove(key, 1, list.get(0));
        List<Object> cached = redisService.lRange(key, 0, 3);
        return CommonResult.success(cached);
    }
}
</code>

Project Source Code

https://github.com/macrozheng/mall-learning/tree/master/mall-tiny-redis

Javabackend developmentRedisSpring Data RedisSpring Cache
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

0 followers
Reader feedback

How this landed with the community

login 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.