Enterprise Java

Using Redis with Spring

As NoSQL solutions are getting more and more popular for many kind of problems, more often the modern projects consider to use some (or several) of NoSQLs instead (or side-by-side) of traditional RDBMS. I have already covered my experience with MongoDB in this, this and this posts. In this post I would like to switch gears a bit towards Redis, an advanced key-value store.
 
Aside from very rich key-value semantics, Redis also supports pub-sub messaging and transactions. In this post I am going just to touch the surface and demonstrate how simple it is to integrate Redis into your Spring application. As always, we will start with Maven POM file for our project:

 

            
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 
 
 <modelversion>4.0.0</modelversion>
 <groupid>com.example.spring</groupid>
 <artifactid>redis</artifactid>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>jar</packaging>

 <properties>
  <project.build.sourceencoding>UTF-8</project.build.sourceencoding>
  <spring.version>3.1.0.RELEASE</spring.version>
 </properties>

 <dependencies>
  <dependency>
   <groupid>org.springframework.data</groupid>
   <artifactid>spring-data-redis</artifactid>
   <version>1.0.0.RELEASE</version>
  </dependency>

  <dependency>
   <groupid>cglib</groupid>
   <artifactid>cglib-nodep</artifactid>
   <version>2.2</version>
  </dependency>

  <dependency>
   <groupid>log4j</groupid>
   <artifactid>log4j</artifactid>
   <version>1.2.16</version>
  </dependency>

  <dependency>
   <groupid>redis.clients</groupid>
   <artifactid>jedis</artifactid>
   <version>2.0.0</version>
   <type>jar</type>
  </dependency>

  <dependency>
   <groupid>org.springframework</groupid>
   <artifactid>spring-core</artifactid>
   <version>${spring.version}</version>
  </dependency>

  <dependency>
   <groupid>org.springframework</groupid>
   <artifactid>spring-context</artifactid>
   <version>${spring.version}</version>
  </dependency>
 </dependencies>
</project>
Spring Data Redis is the another project under Spring Data umbrella which provides seamless injection of Redis into your application. The are several Redis clients for Java and I have chosen the Jedis as it is stable and recommended by Redis team at the moment of writing this post.
We will start with simple configuration and introduce the necessary components first. Then as we move forward, the configuration will be extended a bit to demonstrated pub-sub capabilities. Thanks to Java config support, we will create the configuration class and have all our dependencies strongly typed, no XML anymore:
package com.example.redis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

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

 @Bean
 RedisTemplate< String, Object > redisTemplate() {
  final RedisTemplate< String, Object > template =  new RedisTemplate< String, Object >();
  template.setConnectionFactory( jedisConnectionFactory() );
  template.setKeySerializer( new StringRedisSerializer() );
  template.setHashValueSerializer( new GenericToStringSerializer< Object >( Object.class ) );
  template.setValueSerializer( new GenericToStringSerializer< Object >( Object.class ) );
  return template;
 }
}
That’s basically everything we need assuming we have single Redis server up and running on localhost with default configuration. Let’s consider several common uses cases: setting a key to some value, storing the object and, finally, pub-sub implementation. Storing and retrieving a key/value pair is very simple:
@Autowired private RedisTemplate< String, Object > template;

public Object getValue( final String key ) {
    return template.opsForValue().get( key );
}

public void setValue( final String key, final String value ) {
    template.opsForValue().set( key, value );
}

Optionally, the key could be set to expire (yet another useful feature of Redis), f.e. let our keys expire in 1 second:

public void setValue( final String key, final String value ) {
    template.opsForValue().set( key, value );
    template.expire( key, 1, TimeUnit.SECONDS );
}

Arbitrary objects could be saved into Redis as hashes (maps), f.e. let save instance of some class User

public class User {
 private final Long id;
 private String name;
 private String email;
       
    // Setters and getters are omitted for simplicity
}

into Redis using key pattern “user:<id>”:

public void setUser( final User user ) {
 final String key = String.format( "user:%s", user.getId() );
 final Map< String, Object > properties = new HashMap< String, Object >();

 properties.put( "id", user.getId() );
 properties.put( "name", user.getName() );
 properties.put( "email", user.getEmail() );

 template.opsForHash().putAll( key, properties);
}

Respectively, object could easily be inspected and retrieved using the id.

public User getUser( final Long id ) {
 final String key = String.format( "user:%s", id );

 final String name = ( String )template.opsForHash().get( key, "name" );
 final String email = ( String )template.opsForHash().get( key, "email" );

 return new User( id, name, email );
}
There are much, much more which could be done using Redis, I highly encourage to take a look on it. It surely is not a silver bullet but could solve many challenging problems very easy. Finally, let me show how to use a pub-sub messaging with Redis. Let’s add a bit more configuration here (as part of AppConfig class):
@Bean
MessageListenerAdapter messageListener() {
 return new MessageListenerAdapter( new RedisMessageListener() );
}

@Bean
RedisMessageListenerContainer redisContainer() {
 final RedisMessageListenerContainer container = new RedisMessageListenerContainer();

 container.setConnectionFactory( jedisConnectionFactory() );
 container.addMessageListener( messageListener(), new ChannelTopic( "my-queue" ) );

 return container;
}
The style of message listener definition should look very familiar to Spring users: generally, the same approach we follow to define JMS message listeners. The missed piece is our RedisMessageListener class definition:
package com.example.redis.impl;

import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;

public class RedisMessageListener implements MessageListener {
 @Override
 public void onMessage(Message message, byte[] paramArrayOfByte) {
  System.out.println( "Received by RedisMessageListener: " + message.toString() );
 }
}
Now, when we have our message listener, let see how we could push some messages into the queue using Redis. As always, it’s pretty simple:
@Autowired private RedisTemplate< String, Object > template;

public void publish( final String message ) {
 template.execute(
  new RedisCallback< Long >() {
   @SuppressWarnings( "unchecked" )
   @Override
   public Long doInRedis( RedisConnection connection ) throws DataAccessException {
    return connection.publish(
     ( ( RedisSerializer< String > )template.getKeySerializer() ).serialize( "queue" ),
     ( ( RedisSerializer< Object > )template.getValueSerializer() ).serialize( message ) );
   }
  }
 );
}

That’s basically it for very quick introduction but definitely enough to fall in love with Redis.

Reference: Using Redis with Spring from our JCG partner Andrey Redko at the Andriy Redko {devmind} blog.

Andrey Redko

Andriy is a well-grounded software developer with more then 12 years of practical experience using Java/EE, C#/.NET, C++, Groovy, Ruby, functional programming (Scala), databases (MySQL, PostgreSQL, Oracle) and NoSQL solutions (MongoDB, Redis).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

11 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Amit
Amit
8 years ago

Hi Andrey,
Thanks for this post. could you help me to have a sample code for key expiration notification using spring data redis. I tried to look in to http://redis.io/topics/notifications, but it didn’t help me out. Your RedisMessageListener looks closer to what I was looking for. but I could not co-relate it with expire.
Thanks
Amit

Andriy Redko
8 years ago
Reply to  Amit

Hi Amit,

Thank you for your comment. Right, key expiration notification became available in Redis 2.8.0, way after this blog post has been published. Essentially, I think once you run Redis with notifications enabled, you can subscribe on __keyspace@* or __keyevent@* topics to get notified about expired keys. Is it what you have tried and it didn’t work out?

Thank you.

Best Regards,
Andriy Redko

Amit Mishra
Amit Mishra
8 years ago
Reply to  Andriy Redko

Hi Andriy, Thanks for reply. No I couldn’t get that how to use it in my code. I am trying to write a service which stores all the social count for my site in redis for next two minutes and serves the result from redis as per request. But some how when my keys expire after 2 minutes, and my service doesn’t have way to know, if key is already expired and it should insert the new results. That is why I was planning to implement notification on expire. I read in redis documentation , if you want redis DB… Read more »

Andriy Redko
8 years ago
Reply to  Amit Mishra

Hi Amit, Thanks a lot for providing more details. It is actually very easy to enable Redis key expiration notifications in Redis using configuration or just running this command in Redis CLI: CONFIG SET notify-keyspace-events KEx On the Spring/Java side, you just create pattern topic and subscribe either to __keyspace or __keyevent, whatever is easier to you, for example: @Bean RedisMessageListenerContainer redisNotificationsContainer() { final RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory( jedisConnectionFactory() ); container.addMessageListener( messageListener(), new PatternTopic( “__keyspace@*” ) ); return container; } With that, you should be able to consume key expiration events from Redis (you also can limit it… Read more »

Kesh
Kesh
7 years ago
Reply to  Andriy Redko

Hi Andriy! I’m still very new to Spring Redis. I have the same issue as Amir. However, I don’t know how to configure redis message listener. On what file should I place the configuration for “notify-keyspace-events”? And where should I place the creation of pattern topics? I tried using RedisMessageListenerContainer without manually configuring it (I assumed that spring redis has already configured that. Just like it’s automatic configuration for redis itself). However, I got an error “Field redisMessageListenerContainer in com.sprobe.iaoy.pdf.file.redis.RedisUploadService required a bean of type ‘org.springframework.data.redis.listener.RedisMessageListenerContainer’ that could not be found.”. Problem is I don’t know where to place the… Read more »

kochero iniendo
kochero iniendo
7 years ago

Hey, Thank you for the great example. Used it successfully. But I have trouble writing unit tests for it.

Andriy Redko
7 years ago

Hey!

Thanks a lot for your comment. That’s right, usually Redis integration is mocked in the unit test cases.
I think Mockito is going to help you a lot there.
Thanks!

Best Regards,
Andriy Redko

Desmond Gan
Desmond Gan
7 years ago

Thanks.

Andriy Redko
7 years ago

Hi Kesh, Thanks a lot for your comment. You can place the configuration for “notify-keyspace-events” in any file which is annotated with Spring @Configuration annotation, f.e. @Configuration @Import( value = ApplicationConfiguration.class ) public class PubsubConfiguration { @Bean RedisMessageListenerContainer redisNotificationsContainer() { final RedisMessageListenerContainer container = new RedisMessageListenerContainer(); container.setConnectionFactory( jedisConnectionFactory() ); container.addMessageListener( messageListener(), new PatternTopic( “__keyspace@*” ) ); return container; } @Bean public MessageListener listener() { return new RedisMessageListener(); } } If you are using Spring Boot, the Redis configuration could be discovered either from application.yml or from default settings. The MessageListener instance is the one which you need to implement, you… Read more »

Rakesh
Rakesh
5 years ago

Hi Andrey, Can i get the source code for this?

Andriy Redko
5 years ago
Reply to  Rakesh

Hi Rakesh,

Yes, sure, https://github.com/reta/spring-examples
Thank you!

Best Regards,
Andriy Redko

Back to top button