Introduction to Redis Using Spring Boot

The translation of the article was prepared specifically for students of the course "Developer on the Spring Framework".






In this article, we will cover the basics of using Redis through Spring Boot using the Spring Data Redis library.







We will create an application that demonstrates how to perform CRUD operations through a web interface. The source code for this project is available on GitHub .



What is Redis?



Redis is an open source data warehouse for key-value data structures that can be used as a database, cache, and message broker. In terms of implementation, key-value stores are some of the largest and oldest representatives in the NoSQL world. Redis supports data structures such as strings, hashes, lists, sets, and sorted sets with range queries.



The Spring Data Redis framework makes it easy to write Spring applications that use Redis storage, providing a convenient abstraction of data storage.



Redis Server Setup



Server is available for free here .

If you are using a Mac, you can install it using homebrew



:



 brew install redis
      
      





Then start the server:



 mikes-MacBook-Air:~ mike$ redis-server 10699:C 23 Nov 08:35:58.306 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo 10699:C 23 Nov 08:35:58.307 # Redis version=4.0.2, bits=64, commit=00000000, modified=0, pid=10699, just started 10699:C 23 Nov 08:35:58.307 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf 10699:M 23 Nov 08:35:58.309 * Increased maximum number of open files to 10032 (it was originally set to 256). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 4.0.2 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in standalone mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 6379 | `-._ `._ / _.-' | PID: 10699 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-' 10699:M 23 Nov 08:35:58.312 # Server initialized 10699:M 23 Nov 08:35:58.312 * Ready to accept connections
      
      





Maven dependencies



Let's declare the necessary dependencies in pom.xml



for the application we will work with:



 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
      
      





Redis configuration



We need to connect our application with the Redis server. To establish a connection, we use Jedis , a client implementation of Redis.



Configuration



Let's start with the definition of configuration beans:



 @Bean JedisConnectionFactory jedisConnectionFactory() { return new JedisConnectionFactory(); } @Bean public RedisTemplate<String, Object> redisTemplate() { final RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(jedisConnectionFactory()); template.setValueSerializer(new GenericToStringSerializer<Object>(Object.class)); return template; }
      
      





JedisConnectionFactory



is presented as a bean, so that we can create a RedisTemplate



to request data.



Post Publisher



Following the principles of SOLID , we create the MessagePublisher



interface:



 public interface MessagePublisher { void publish(final String message); }
      
      





We implement the MessagePublisher



interface using the high-level RedisTemplate to publish a message, since RedisTemplate



allows RedisTemplate



to send arbitrary objects as messages:



 @Service public class MessagePublisherImpl implements MessagePublisher { @Autowired private RedisTemplate<String, Object> redisTemplate; @Autowired private ChannelTopic topic; public MessagePublisherImpl() { } public MessagePublisherImpl(final RedisTemplate<String, Object> redisTemplate, final ChannelTopic topic) { this.redisTemplate = redisTemplate; this.topic = topic; } public void publish(final String message) { redisTemplate.convertAndSend(topic.getTopic(), message); } }
      
      





We also define this as a bean in RedisConfig



:



 @Bean MessagePublisher redisPublisher() { return new MessagePublisherImpl(redisTemplate(), topic()); }
      
      





Message recipient



To subscribe to messages, you need to implement the MessageListener



interface: each time a new message arrives, the user code located in the onMessage



method is onMessage



. This interface provides access to the message, the channel through which it was received, and allows you to use any template used to subscribe to the channel.



 @Service public class MessageSubscriber implements MessageListener { public static List<String> messageList = new ArrayList<String>(); public void onMessage(final Message message, final byte[] pattern) { messageList.add(message.toString()); System.out.println("Message received: " + new String(message.getBody())); } }
      
      





Also, this class must be registered as a bean in RedisConfig



:



 @Bean MessageListenerAdapter messageListener() { return new MessageListenerAdapter(new MessageSubscriber()); }
      
      





Redisrepository



Now that we have set up the application to interact with the Redis server, we will prepare the application for receiving test data.



Model



For this example, we define a Movie



model with two fields:



 private String id; private String name; //standard getters and setters
      
      





Repository interface



Unlike other Spring Data projects, Spring Data Redis provides everything you need to work on top of other Spring Data interfaces. This may look odd for people with experience with other Spring Data projects.



Often there is no need to write a repository interface implementation with Spring Data projects. We just interact with the interface. Spring Data JPA provides numerous repository interfaces that can be extended to provide features such as CRUD operations, derived queries, and pagination.



So, unfortunately, we need to write our own interface, and then define the methods :



 public interface RedisRepository { Map<Object, Object> findAllMovies(); void add(Movie movie); void delete(String id); Movie findMovie(String id); }
      
      





Repository Implementation



The class uses redisTemplate



defined in the RedisConfig



configuration RedisConfig



.



We use HashOperations



, which Spring Data Redis offers:



 @Repository public class RedisRepositoryImpl implements RedisRepository { private static final String KEY = "Movie"; private RedisTemplate<String, Object> redisTemplate; private HashOperations hashOperations; @Autowired public RedisRepositoryImpl(RedisTemplate<String, Object> redisTemplate){ this.redisTemplate = redisTemplate; } @PostConstruct private void init(){ hashOperations = redisTemplate.opsForHash(); } public void add(final Movie movie) { hashOperations.put(KEY, movie.getId(), movie.getName()); } public void delete(final String id) { hashOperations.delete(KEY, id); } public Movie findMovie(final String id){ return (Movie) hashOperations.get(KEY, id); } public Map<Object, Object> findAllMovies(){ return hashOperations.entries(KEY); } }
      
      





Let's pay attention to the init()



method. In this method, we use a function called opsForHash()



, it returns operations performed with hash values opsForHash()



with this key. Then we use hashOps



, which was defined in init()



, for all of our CRUD operations.



Web interface



In this section, we will look at adding Redis CRUD capabilities to the web interface.



Adding a movie



We want to be able to add a movie through a web page. The key is the identifier of the movie, and the value is the actual object. However, we will return to this later, so only the name of the movie is displayed as the value.



Let's add a form to the HTML document and assign the appropriate names and identifiers:



 <form id="addForm"> <div class="form-group"> <label for="keyInput">Movie ID (key)</label> <input name="keyInput" id="keyInput" class="form-control"/> </div> <div class="form-group"> <label for="valueInput">Movie Name (field of Movie object value)</label> <input name="valueInput" id="valueInput" class="form-control"/> </div> <button class="btn btn-default" id="addButton">Add</button> </form>
      
      





Now we use JavaScript to save values ​​when submitting the form:



 $(document).ready(function() { var keyInput = $('#keyInput'), valueInput = $('#valueInput'); refreshTable(); $('#addForm').on('submit', function(event) { var data = { key: keyInput.val(), value: valueInput.val() }; $.post('/add', data, function() { refreshTable(); keyInput.val(''); valueInput.val(''); keyInput.focus(); }); event.preventDefault(); }); keyInput.focus(); });
      
      





Set the @RequestMapping



parameters for the POST request, request the key and value, create a Movie



object and save it in the repository:



 @RequestMapping(value = "/add", method = RequestMethod.POST) public ResponseEntity<String> add( @RequestParam String key, @RequestParam String value) { Movie movie = new Movie(key, value); redisRepository.add(movie); return new ResponseEntity<>(HttpStatus.OK); }
      
      





View Content



Once the Movie object is added, we update the table to display the new values. In the block of JavaScript code, we called the refreshTable()



function. It performs a GET request to get the current data in the repository:



 function refreshTable() { $.get('/values', function(data) { var attr, mainTable = $('#mainTable tbody'); mainTable.empty(); for (attr in data) { if (data.hasOwnProperty(attr)) { mainTable.append(row(attr, data[attr])); } } }); }
      
      





The GET request is processed by the findAll()



method, which retrieves all Movie objects stored in the store, and then converts the data type from Map <Object, Object>



to Map <String, String>



:



 @RequestMapping("/values") public @ResponseBody Map<String, String> findAll() { Map<Object, Object> aa = redisRepository.findAllMovies(); Map<String, String> map = new HashMap<String, String>(); for(Map.Entry<Object, Object> entry : aa.entrySet()){ String key = (String) entry.getKey(); map.put(key, aa.get(key).toString()); } return map; }
      
      





Delete movie



We will write a script to execute a POST request along the /delete



path, update the table and switch keyboard focus for convenient input:



 function deleteKey(key) { $.post('/delete', {key: key}, function() { refreshTable(); $('#keyInput').focus(); }); }
      
      





We request a key and delete the object in redisRepository



based on this key:



 @RequestMapping(value = "/delete", method = RequestMethod.POST) public ResponseEntity<String> delete(@RequestParam String key) { redisRepository.delete(key); return new ResponseEntity<>(HttpStatus.OK); }
      
      





Demo



Here we added two films:







And one movie was deleted:







Conclusion



In this tutorial, we looked at Spring Data Redis and one way to connect it to a web application to perform CRUD operations.



The source code for the sample application is on GitHub .



That's all. Traditionally waiting for your comments.



All Articles