I usually post about Persistence on Twitter - you can follow me there:
1. Overview
This article is an introduction to Spring Data Redis, which provides the abstractions of the Spring Data platform to Redis – the popular in-memory data structure store.
Redis is driven by a keystore-based data structure to persist data and can be used as a database, cache, message broker, etc.
We’ll be able to use the common patterns of Spring Data (templates, etc.), while also having the traditional simplicity of all Spring Data projects.
2. Maven Dependencies
Let’s start by declaring the Spring Data Redis dependencies in the pom.xml:
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.6.2.RELEASE</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.5.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.2.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.2.2.RELEASE</version> </dependency>
3. The Redis Configuration
Redis clients are used to define the connection settings between the application client and the Redis server instance.
There are number of Redis client implementations available for Java. In this tutorial, we’ll use Jedis – a simple and powerful Redis client implementation.
There is good support for both XML and Java configuration in the framework; for this tutorial, we’ll use Java-based configuration.
3.1. Java Configuration
Let’s start with the configuration bean definitions:
@Bean JedisConnectionFactory jedisConnectionFactory() { return new JedisConnectionFactory(); } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(jedisConnectionFactory()); return template; }
The configuration is quite simple. First, using the Jedis client, a Redis connectionFactory can be defined.
Using the jedisConnectionFactory, a RedisTemplate is defined, which will then be used for querying data with a custom repository.
3.2. Custom Connection Properties
You may have already noticed that the usual connection-related properties are missing in the above configuration. For example, the server address and port are missing in the configuration.The reason is simple: for our example, we are using the defaults.
However, if you need to configure the connection details, you can always modify the jedisConnectionFactory configuration as follows:
@Bean JedisConnectionFactory jedisConnectionFactory() { JedisConnectionFactory jedisConFactory = new JedisConnectionFactory(); jedisConFactory.setHostName("localhost"); jedisConFactory.setPort(6379); return jedisConFactory; }
Let’s use a Student entity for our examples.
public class Student implements Serializable { public enum Gender { MALE, FEMALE } private String id; private String name; private Gender gender; private int grade; ... }
4.1. The Spring Data Repository
Let’s now create the StudentRepository as follows:
public interface StudentRepository { void saveStudent(Student person); ... }
Note that, unlike other Spring Data Repository interfaces, this is just a standard interface to define a supported method. It doesn’t enable any Spring-related features.
And this is certainly unusual for a Spring Data project. Most of the other Spring Data projects are capable of building repositories based on the common Spring Data interfaces.
For example, Spring Data JPA provides several base repository interfaces that you can extend to get base features such basic CRUD operations, the ability to generate queries based on method names, etc. In most cases, there’s no need to write an implementation of the repository interface at all.
Spring Data Redis, however, does not have base repository interfaces to extend, nor does it have method name-based query generation. This partly has to do with the team who works on Spring Data Redis simply not having enough time to work on it, and partly has to do with the fact that Redis does not support “queries” the way we typically think of them. You can read more in this StackOverflow answer by one of the maintainers of the Spring Data Redis project.
4.2. A Repository Implementation
The StudentRepositoryImpl implementation is using the redisTemplate defined in the Java configuration above.
Redis supports different data structures such as strings, hashes, lists, sets, and sorted sets. In this example we will use the opsForHash() function, which uses hash-related operations for data manipulation. We will use the string “Student” as the name of the hash in which our Student entities will be stored.
@Repository public class StudentRepositoryImpl implements StudentRepository { private static final String KEY = "Student"; private RedisTemplate<String, Student> redisTemplate; private HashOperations hashOps; @Autowired private StudentRepositoryImpl(RedisTemplate redisTemplate) { this.redisTemplate = redisTemplate; } @PostConstruct private void init() { hashOps = redisTemplate.opsForHash(); } public void saveStudent(Student student) { hashOps.put(KEY, student.getId(), student); } public void updateStudent(Student student) { hashOps.put(KEY, student.getId(), student); } public Student findStudent(String id) { return (Student) hashOps.get(KEY, id); } public Map<Object, Object> findAllStudents() { return hashOps.entries(KEY); } public void deleteStudent(String id) { hashOps.delete(KEY, id); } }
The opsForHash() function returns the operations performed on hash values bound to the given key. It’s defined with three parameters: the key, the hash key, and the hash value type.
Just note that, in addition to opsForHash(), there are similar set-related, list-related, and simple value functions.
5. Data Access using StudentRepository
We have implemented the data accessing behaviors on the StudentRepositoryImpl class so that it can be used directly to persist data or manipulate persisted data.
5.1. Saving a New Student Object
Let’s save a new student object in data store:
Student student = new Student( "Eng2015001", "John Doe", Student.Gender.MALE, 1); studentRepository.saveStudent(student);
5.2. Retrieving an Existing Student Object
We can verify the correct insertion of the student in the previous section by fetching the inserted student data:
Student retrievedStudent = studentRepository.findStudent("Eng2015001");
5.3. Updating an Existing Student Object
Let’s change the name of the student retrieved above and save it again:
retrievedStudent.setName("Richard Watson"); studentRepository.saveStudent(student);
Finally you can retrieve the student’s data again and verify that the name is updated in the datastore.
5.4. Deleting an Existing Student Data
We can delete the above-inserted student data:
studentRepository.deleteStudent(student.getId());
Now we can search for the student object and verify that the result is null.
5.5. Find All Student Data
We can insert a few student objects:
Student engStudent = new Student("Eng2015001", "John Doe", Student.Gender.MALE, 1); Student medStudent = new Student("Med2015001", "Gareth Houston", Student.Gender.MALE, 2); studentRepository.saveStudent(engStudent); studentRepository.saveStudent(medStudent);
However this can also be done by inserting a Map object. For that, there is a different method – putAll() – which accepts a single Map object containing multiple student objects to be persisted.
To find all inserted students:
Map<Object, Object> retrievedStudents = studentRepository.findAllStudents();
Then we can easily check the size of the retrievedStudents or verify for a greater granularity by checking the properties of the each object.
6. Conclusion
In this tutorial, we went through the basics of Spring Data Redis. The source code of the examples above can be found in a GitHub project.