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;
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.