Spring Data Redis Cache
November 30, 2023
On this page I will create Spring Data application using Redis Cache. We willl learn to integrate Redis with Spring Data application and perform caching for our data obtained from database.
1. Maven Depedency
Find the Maven file used in the demo application.<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2. Redis Connection Factory
To get connections for Redis server, Spring providesLettuceConnectionFactory
and JedisConnectionFactory
.
1. Using LettuceConnectionFactory
LettuceConnectionFactory
creates Lettuce connections. It requires lettuce-core
which is provided by Spring Boot 2.0 by default using the starter spring-boot-starter-data-redis
. Find the LettuceConnectionFactory
bean in JavaConfig.
@Bean public LettuceConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration(); redisConf.setHostName("localhost"); redisConf.setPort(6379); return new LettuceConnectionFactory(redisConf); }
RedisStandaloneConfiguration
we configure Redis connections via Redis connection factory.
Find the XML configuration for
LettuceConnectionFactory
bean.
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" p:host-name="localhost" p:port="6379" />
JedisConnectionFactory
provides Jedis based connections. In Spring Boot 2.0, spring-boot-starter-data-redis
provides Lettuce by default instead of Jedis. So to work with Jedis, we need to include Jedis dependency in our build file.
Using Maven
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
compile 'redis.clients:jedis:2.9.0'
JedisConnectionFactory
bean in our JavaConfig.
@Bean public JedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration(); redisConf.setHostName("localhost"); redisConf.setPort(6379); return new JedisConnectionFactory(redisConf); }
JedisConnectionFactory
bean.
<bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name="localhost" p:port="6379" />
3. RedisCacheManager
RedisCacheManager
is the CacheManager
backed by Redis. It is instantiated using RedisCacheManager.create()
or RedisCacheManager.builder()
by passing connection factory. To use it we need to create RedisCacheManager
bean in our JavaConfig.
@Bean public RedisCacheManager cacheManager() { RedisCacheManager rcm = RedisCacheManager.create(redisConnectionFactory()); rcm.setTransactionAware(true); return rcm; }
setTransactionAware
method decides whether cache objects should be transaction-aware or not. The default value is false
and when we set setTransactionAware(true)
, then cache put and evict operations are synchronized with ongoing Spring managed transactions. It means cache manager will put or evict caches only after commit of successful transactions.
Find the XML configuration for
RedisCacheManager
bean.
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" factory-method="create" c:connection-factory-ref="redisConnectionFactory" p:transaction-aware="true"/>
4. Enable Caching
To enable cache abstraction in our application, Spring provides@EnableCaching
annotation. @EnableCaching
enables annotation-driven cache management capability. It is equivalent to XML <cache:annotation-driven/>
. They are responsible to register required Spring components to enable annotation-driven cache management. @EnableCaching
is annotated with @Configuration
. Find the JavaConfig for Redis configuration annotated with @EnableCaching
.
RedisConfig.java
@Configuration @EnableCaching @PropertySource("classpath:redis.properties") public class RedisConfig { @Autowired private Environment env; @Bean public LettuceConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisConf = new RedisStandaloneConfiguration(); redisConf.setHostName(env.getProperty("redis.host")); redisConf.setPort(Integer.parseInt(env.getProperty("redis.port"))); redisConf.setPassword(RedisPassword.of(env.getProperty("redis.password"))); return new LettuceConnectionFactory(redisConf); } @Bean public RedisCacheManager cacheManager() { RedisCacheManager rcm = RedisCacheManager.create(redisConnectionFactory()); rcm.setTransactionAware(true); return rcm; } }
<cache:annotation-driven/>
.
redis-config.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xmlns:context="http://www.springframework.org/schema/context" xmlns:cache="http://www.springframework.org/schema/cache" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd"> <cache:annotation-driven/> <context:property-placeholder location="classpath:redis.properties" ignore-unresolvable="true"/> <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory" p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.password}" /> <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager" factory-method="create" c:connection-factory-ref="redisConnectionFactory" p:transaction-aware="true"/> </beans>
5. Creating Cache
Find the code to perform caching.ArticleService.java
@Service public class ArticleService implements IArticleService { @Autowired private ArticleRepository articleRepository; @Override @Cacheable(value= "articleCache", key= "#articleId") public Article getArticleById(long articleId) { System.out.println("--- Inside getArticleById() ---"); return articleRepository.findById(articleId).get(); } @Override @Cacheable(value= "allArticlesCache", unless= "#result.size() == 0") public List<Article> getAllArticles(){ System.out.println("--- Inside getAllArticles() ---"); List<Article> list = new ArrayList<>(); articleRepository.findAll().forEach(e -> list.add(e)); return list; } @Override @Caching( put= { @CachePut(value= "articleCache", key= "#article.articleId") }, evict= { @CacheEvict(value= "allArticlesCache", allEntries= true) } ) public Article addArticle(Article article){ System.out.println("--- Inside addArticle() ---"); return articleRepository.save(article); } @Override @Caching( put= { @CachePut(value= "articleCache", key= "#article.articleId") }, evict= { @CacheEvict(value= "allArticlesCache", allEntries= true) } ) public Article updateArticle(Article article) { System.out.println("--- Inside updateArticle() ---"); return articleRepository.save(article); } @Override @Caching( evict= { @CacheEvict(value= "articleCache", key= "#articleId"), @CacheEvict(value= "allArticlesCache", allEntries= true) } ) public void deleteArticle(long articleId) { System.out.println("--- Inside deleteArticle() ---"); articleRepository.delete(articleRepository.findById(articleId).get()); } }