Skip to content

Commit 5a25e80

Browse files
christophstroblodrotbohm
authored andcommitted
spring-projects#149 - Add Redis repository support sample.
Bumped Spring Data Redis version to 1.7 RC1. Added JUnit Rule for Embedded Redis Server. Original pull request: spring-projects#162.
1 parent 1f94bb1 commit 5a25e80

File tree

13 files changed

+765
-0
lines changed

13 files changed

+765
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ We have separate folders for the samples of individual modules:
3434

3535
* `example` - Example for basic Spring Data Redis setup.
3636
* `cluster-sentinel` - Example for Redis cluster and Sentinel support.
37+
* `repository` - Example demonstrating Spring Data repository abstraction on top of Redis.
3738

3839
## Spring Data Elasticsearch
3940

redis/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<module>cluster-sentinel</module>
2121
<module>example</module>
2222
<module>cluster</module>
23+
<module>repository</module>
2324
</modules>
2425

2526
<dependencies>

redis/repository/README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
# Spring Data Redis - Repository Examples #
2+
3+
This project contains examples for Spring Data specific repository abstraction on top of Redis.
4+
5+
## Repository Suport ##
6+
7+
Redis Repository support allows to convert, store, retrieve and index entities within Redis native data structures. To do, besides the `HASH` containing the actual properties several [Secondary Index](http://redis.io/topics/indexes) structures are set up and maintained.
8+
9+
```java
10+
@RedisHash("persons")
11+
class Person {
12+
13+
@Id String id;
14+
15+
@Indexed String firstname;
16+
@Indexed String lastname;
17+
18+
Gender gender;
19+
Address address;
20+
21+
@Reference List<Person> children;
22+
}
23+
```
24+
25+
The above entity would for example then be stored in a Redis [HASH](http://redis.io/topics/data-types#hashes) with key `persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d`.
26+
27+
```properties
28+
_class=example.springdata.redis.domain.Person <1>
29+
id=9b0ed8ee-14be-46ec-b5fa-79570aadb91d
30+
firstname=eddard <2>
31+
lastname=stark
32+
gender=MALE
33+
address.city=winterfell <3>
34+
address.country=the north
35+
children.[0]=persons:41436096-aabe-42fa-bd5a-9a517fbf0260 <4>
36+
children.[1]=persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53
37+
children.[2]=persons:440b24c6-ede2-495a-b765-2d8b8d6e3995
38+
```
39+
```
40+
<1> The _class attribute is used to store the actual type and is required for object/hash conversion.
41+
<2> Values are also included in Secondary Index when annotated with @Indexed.
42+
<3> Complex types are flattened out and embedded into the HASH as long as there is no explicit Converter registered or a @Reference annotation present.
43+
<4> Using @Reference stores only the key of a referenced object without embedding values like in <3>.
44+
```
45+
46+
Additionally indexes are created for `firstname`, `lastname` and `address.city` containing the `id` of the actual entity.
47+
48+
```bash
49+
redis/src $ ./redis-cli keys *
50+
1) "persons" <1>
51+
2) "persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d" <2>
52+
3) "persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d:idx" <3>
53+
4) "persons:41436096-aabe-42fa-bd5a-9a517fbf0260"
54+
5) "persons:41436096-aabe-42fa-bd5a-9a517fbf0260:idx"
55+
6) "persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53"
56+
7) "persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53:idx"
57+
8) "persons:440b24c6-ede2-495a-b765-2d8b8d6e3995"
58+
9) "persons:440b24c6-ede2-495a-b765-2d8b8d6e3995:idx"
59+
10) "persons:firstname:eddard" <4>
60+
11) "persons:firstname:robb"
61+
12) "persons:firstname:sansa"
62+
13) "persons:firstname:arya"
63+
14) "persons:lastname:stark" <5>
64+
15) "persons:address.city:winterfell" <6>
65+
```
66+
```
67+
<1> SET holding all ids known in the keyspace "persons".
68+
<2> HASH holding property values for id "9b0ed8ee-14be-46ec-b5fa-79570aadb91d" in keyspace "persons".
69+
<3> SET holding index information for CRUD operations.
70+
<4> SET used for indexing entities with "firstname" equal to "eddard" within keyspace "persons".
71+
<5> SET used for indexing entities with "lastname" equal to "stark" within keyspace "persons".
72+
<6> SET used for indexing entities with "address.city" equal to "winterfell" within keyspace "persons".
73+
```
74+
75+
## Configuration ##
76+
77+
The below configuration uses [Jedis](https://github.com/xetorthio/jedis) to connect to Redis on its default port. Please note the usage of `@EnableRedisRepositories` to create `Repository` instances.
78+
79+
```java
80+
@Configuration
81+
@EnableRedisRepositories
82+
class AppConfig {
83+
84+
@Bean
85+
RedisConnectionFactory connectionFactory() {
86+
return new JedisConnectionFactory();
87+
}
88+
89+
@Bean
90+
RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
91+
92+
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
93+
template.setConnectionFactory(connectionFactory);
94+
95+
return template;
96+
}
97+
}
98+
```
99+
100+
Having the infrastructure in place you can go on declaring and using the `Repository` interface.
101+
102+
```java
103+
interface PersonRepository extends CrudRepository<Person, String> {
104+
105+
List<Person> findByLastname(String lastname);
106+
107+
Page<Person> findByLastname(String lastname, Pageable page);
108+
109+
List<Person> findByFirstnameAndLastname(String firstname, String lastname);
110+
111+
List<Person> findByFirstnameOrLastname(String firstname, String lastname);
112+
113+
List<Person> findByAddress_City(String city);
114+
}
115+
```
116+
117+
## One Word of Caution ##
118+
119+
Maintaining complex types and index structures stands and falls with its usage. Please consider the following:
120+
121+
* Nested document structures increase object <> hash conversion overhead.
122+
* Consider having only those indexes you really need instead of indexing each and every property.
123+
* Pagination is a costly operation since the total number of elements is calculated before returning just a slice of data.
124+
* Pagination gives no guarantee on information as elements might be added, moved or removed.

redis/repository/pom.xml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
5+
<artifactId>spring-data-redis-repository-example</artifactId>
6+
<name>Spring Data Redis - Repository Support Example</name>
7+
8+
<parent>
9+
<groupId>org.springframework.data.examples</groupId>
10+
<artifactId>spring-data-redis-examples</artifactId>
11+
<version>1.0.0.BUILD-SNAPSHOT</version>
12+
<relativePath>../pom.xml</relativePath>
13+
</parent>
14+
15+
<dependencies>
16+
17+
<dependency>
18+
<groupId>org.springframework.boot</groupId>
19+
<artifactId>spring-boot-starter-data-redis</artifactId>
20+
</dependency>
21+
<dependency>
22+
<groupId>${project.groupId}</groupId>
23+
<artifactId>spring-data-redis-example-utils</artifactId>
24+
<version>${project.version}</version>
25+
<scope>test</scope>
26+
</dependency>
27+
<dependency>
28+
<groupId>com.github.kstyrc</groupId>
29+
<artifactId>embedded-redis</artifactId>
30+
<version>0.6</version>
31+
<scope>test</scope>
32+
</dependency>
33+
<dependency>
34+
<groupId>org.springframework.data</groupId>
35+
<artifactId>spring-data-redis</artifactId>
36+
<version>1.7.0.RC1</version>
37+
</dependency>
38+
<dependency>
39+
<groupId>org.springframework.data</groupId>
40+
<artifactId>spring-data-keyvalue</artifactId>
41+
<version>1.1.0.RC1</version>
42+
</dependency>
43+
<dependency>
44+
<groupId>org.springframework.data</groupId>
45+
<artifactId>spring-data-commons</artifactId>
46+
<version>1.12.0.RC1</version>
47+
</dependency>
48+
</dependencies>
49+
50+
</project>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package example.springdata.redis.domain;
17+
18+
import lombok.Data;
19+
import lombok.EqualsAndHashCode;
20+
21+
import org.springframework.data.redis.core.index.Indexed;
22+
23+
/**
24+
* @author Christoph Strobl
25+
*/
26+
@Data
27+
@EqualsAndHashCode
28+
class Address {
29+
30+
private @Indexed String city;
31+
private String country;
32+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package example.springdata.redis;
17+
18+
import org.springframework.context.annotation.Bean;
19+
import org.springframework.context.annotation.Configuration;
20+
import org.springframework.data.redis.connection.RedisConnectionFactory;
21+
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
22+
import org.springframework.data.redis.core.RedisTemplate;
23+
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
24+
25+
/**
26+
* @author Christoph Strobl
27+
*/
28+
@Configuration
29+
@EnableRedisRepositories
30+
public class AppConfig {
31+
32+
/**
33+
* {@link RedisConnectionFactory} to be used when connecting to redis.
34+
*
35+
* @return
36+
*/
37+
@Bean
38+
RedisConnectionFactory connectionFactory() {
39+
return new JedisConnectionFactory();
40+
}
41+
42+
/**
43+
* @param connectionFactory
44+
* @return
45+
*/
46+
@Bean
47+
RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
48+
49+
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
50+
template.setConnectionFactory(connectionFactory);
51+
52+
return template;
53+
}
54+
55+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package example.springdata.redis.domain;
17+
18+
/**
19+
* @author Christoph Strobl
20+
*/
21+
enum Gender {
22+
FEMALE, MALE
23+
}

0 commit comments

Comments
 (0)