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:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | < 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 > |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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; } } |
1 2 3 4 5 6 7 8 9 | @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 ); } |
1 2 3 4 | public void setValue( final String key, final String value ) { template.opsForValue().set( key, value ); template.expire( key, 1 , TimeUnit.SECONDS ); } |
1 2 3 4 5 6 7 | public class User { private final Long id; private String name; private String email; // Setters and getters are omitted for simplicity } |
1 2 3 4 5 6 7 8 9 10 | 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); } |
1 2 3 4 5 6 7 8 | 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 ); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | @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; } |
1 2 3 4 5 6 7 8 9 10 11 | 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() ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @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 ) ); } } ); } |