Redis (Remote Dictionary Server) is deservedly considered an old man in the world of NoSql solutions. This post is about how Spring Data works with it. The idea of ββwriting this post arose because Redis is not quite like a familiar database, it supports data types that are not convenient to use for storing objects (cache does not count) and to search for certain fields. Here, with examples, I will try to describe how Spring Data works with it through the familiar CrudRepository and QueryDSL. This is not an example of HowTo, of which there are many. Who is interested in the insides go further.
The examples will be based on a simple project . Redis rises in a docker container, a spring-boot application that also communicates with it in the container. The application contains a simple model, repository, service and controller. You can touch all this through swagger on localhost: 8080.
In addition to the commands that the service executes to the database, I will also provide a small pseudo-code that more clearly describes what is happening.
We will work with the Student entity :
@Data
@AllArgsConstructor
@NoArgsConstructor
@RedisHash("Student")
public class Student {
@Id
private String id;
private String name;
private int age;
}
Here, it is necessary to clarify that the annotation @RedisHash("Student")
says under which key all entities will be aggregated.
Let's try to keep the first student:
curl -X POST "http://localhost:8080/save" -H "accept: */*" -H "Content-Type: application/json" -d "{\"id\":\"1\",\"name\":\"Stephen\",\"age\":12}"
3 commands were executed:
"DEL" "Student:1"
"HMSET" "Student:1" "_class" "com.odis.redisserviceweb.model.Student" "id" "1" "name" "Stephen" "age" "12"
"SADD" "Student" "1"
, - "DEL" "Student:1"
, "Student:1"
. @RedisHash
+ @Id
.
"HMSET" "Student:1" "_class" "com.odis.redisserviceweb.model.Student" "id" "1" "name" "Stephen" "age" "12".
"Student:1"
. -
Map "Student:1";
"Student:1".put("_class", "com.odis.redisserviceweb.model.Student");
"Student:1".put("id", "1");
"Student:1".put("name", "Stephen");
"Student:1".put("age", "12");
- "SADD" "Student" "1"
- "Student"
"1"
.
? Redis. - "Student:1"
, - "Student"
.
keys * - ( ) :
127.0.0.1:6379> keys *
1) "Student"
2) "Student:1"
, :
127.0.0.1:6379> type "Student"
set
127.0.0.1:6379> type "Student:1"
hash
- ? .
@Id
:
curl -X GET "http://localhost:8080/get/1" -H "accept: */*"
"Student:1" , :
"HGETALL" "Student:1"
1) "_class"
2) "com.odis.redisserviceweb.model.Student"
3) "id"
4) "1"
5) "name"
6) "Stephen"
7) "age"
8) "12"
, , :
curl -X POST "http://localhost:8080/save" -H "accept: */*" -H "Content-Type: application/json" -d "{\"id\":\"2\",\"name\":\"Macaulay\",\"age\":40}"
curl -X GET "http://localhost:8080/get" -H "accept: */*"
3 :
"SMEMBERS" "Student"
1) "1"
2) "2"
"HGETALL" "Student:1"
1) "_class"
2) "com.odis.redisserviceweb.model.Student"
3) "id"
4) "1"
5) "name"
6) "Stephen"
7) "age"
8) "12"
127.0.0.1
"HGETALL" "Student:2"
1) "_class"
2) "com.odis.redisserviceweb.model.Student"
3) "id"
4) "2"
5) "name"
6) "Macaulay"
7) "age"
8) "40"
. - "Student"
- , "Student:@Id"
. , O (N) N - .
:
curl -X DELETE "http://localhost:8080/delete/1" -H "accept: */*"
:
"HGETALL" "Student:1"
1) "_class"
2) "com.odis.redisserviceweb.model.Student"
3) "id"
4) "1"
5) "name"
6) "Stephen"
7) "age"
8) "12"
"DEL" "Student:1"
(integer) 1
"SREM" "Student" "1"
(integer) 1
"SMEMBERS" "Student:1:idx"
(empty array)
"DEL" "Student:1:idx"
(integer) 0
, Id
. "Student"
"1"
.
Student:1:idx
. . , . :
List<Student> findAllByName(String name);
:
curl -X POST "http://localhost:8080/save" -H "accept: */*" -H "Content-Type: application/json" -d "{\"id\":\"1\",\"name\":\"Stephen\",\"age\":12}"
curl -X GET "http://localhost:8080/get/filter/Stephen" -H "accept: */*"
, Redis :
"SINTER" "Student:name:Stephen"
(empty array)
"SINTER"
- , , - "Student:name:Stephen"
.
, , @Id
, @Indexed
Spring Data , . . Redis . name :
@Data
@AllArgsConstructor
@NoArgsConstructor
@RedisHash("Student")
public class Student {
@Id
private String id;
@Indexed
private String name;
private int age;
}
:
curl -X POST "http://localhost:8080/save" -H "accept: */*" -H "Content-Type: application/json" -d "{\"id\":\"1\",\"name\":\"Stephen\",\"age\":12}"
:
"DEL" "Student:1"
"HMSET" "Student:1" "_class" "com.odis.redisserviceweb.model.Student" "id" "1" "name" "Stephen" "age" "12"
"SADD" "Student" "1"
"SADD" "Student:name:Stephen" "1"
"SADD" "Student:1:idx" "Student:name:Stephen"
, : "Student:name:Stephen"
, , @Indexed
. Id
. Id
Stephen
Id
. , . - :
Map "Student:1";
"Student:1".put("_class", "com.odis.redisserviceweb.model.Student");
"Student:1".put("id", "1");
"Student:1".put("name", "Stephen");
"Student:1".put("age", "12");
Set "Student";
"Student".add("1");
Set "Student:name:Stephen";
"Student:name:Stephen".add("1");
Set "Student:1:idx";
"Student:1:idx".add("Student:name:Stephen");
, , Redis :
"SINTER" "Student:name:Stephen"
"HGETALL" "Student:1"
Id
, . .
SINTER
. id
.
.
, Spring Data Redis. Spring.
The disadvantage is that the fields that will be searched for must be marked @Indexed
with an annotation from the very beginning. Otherwise, "indexes" will only be created for features that are retained after adding this annotation. And yes, I understand that Redis is not the best solution for such needs, but if due to a certain situation it will be necessary to use it, then SpringData will be able to do it quite well.