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.

Source: https://habr.com/ru/post/463365/


All Articles