#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: #162.
This commit is contained in:
committed by
Oliver Gierke
parent
1f94bb12f1
commit
5a25e80bbf
@@ -34,6 +34,7 @@ We have separate folders for the samples of individual modules:
|
||||
|
||||
* `example` - Example for basic Spring Data Redis setup.
|
||||
* `cluster-sentinel` - Example for Redis cluster and Sentinel support.
|
||||
* `repository` - Example demonstrating Spring Data repository abstraction on top of Redis.
|
||||
|
||||
## Spring Data Elasticsearch
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
<module>cluster-sentinel</module>
|
||||
<module>example</module>
|
||||
<module>cluster</module>
|
||||
<module>repository</module>
|
||||
</modules>
|
||||
|
||||
<dependencies>
|
||||
|
||||
124
redis/repository/README.md
Normal file
124
redis/repository/README.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# Spring Data Redis - Repository Examples #
|
||||
|
||||
This project contains examples for Spring Data specific repository abstraction on top of Redis.
|
||||
|
||||
## Repository Suport ##
|
||||
|
||||
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.
|
||||
|
||||
```java
|
||||
@RedisHash("persons")
|
||||
class Person {
|
||||
|
||||
@Id String id;
|
||||
|
||||
@Indexed String firstname;
|
||||
@Indexed String lastname;
|
||||
|
||||
Gender gender;
|
||||
Address address;
|
||||
|
||||
@Reference List<Person> children;
|
||||
}
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
```properties
|
||||
_class=example.springdata.redis.domain.Person <1>
|
||||
id=9b0ed8ee-14be-46ec-b5fa-79570aadb91d
|
||||
firstname=eddard <2>
|
||||
lastname=stark
|
||||
gender=MALE
|
||||
address.city=winterfell <3>
|
||||
address.country=the north
|
||||
children.[0]=persons:41436096-aabe-42fa-bd5a-9a517fbf0260 <4>
|
||||
children.[1]=persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53
|
||||
children.[2]=persons:440b24c6-ede2-495a-b765-2d8b8d6e3995
|
||||
```
|
||||
```
|
||||
<1> The _class attribute is used to store the actual type and is required for object/hash conversion.
|
||||
<2> Values are also included in Secondary Index when annotated with @Indexed.
|
||||
<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.
|
||||
<4> Using @Reference stores only the key of a referenced object without embedding values like in <3>.
|
||||
```
|
||||
|
||||
Additionally indexes are created for `firstname`, `lastname` and `address.city` containing the `id` of the actual entity.
|
||||
|
||||
```bash
|
||||
redis/src $ ./redis-cli keys *
|
||||
1) "persons" <1>
|
||||
2) "persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d" <2>
|
||||
3) "persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d:idx" <3>
|
||||
4) "persons:41436096-aabe-42fa-bd5a-9a517fbf0260"
|
||||
5) "persons:41436096-aabe-42fa-bd5a-9a517fbf0260:idx"
|
||||
6) "persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53"
|
||||
7) "persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53:idx"
|
||||
8) "persons:440b24c6-ede2-495a-b765-2d8b8d6e3995"
|
||||
9) "persons:440b24c6-ede2-495a-b765-2d8b8d6e3995:idx"
|
||||
10) "persons:firstname:eddard" <4>
|
||||
11) "persons:firstname:robb"
|
||||
12) "persons:firstname:sansa"
|
||||
13) "persons:firstname:arya"
|
||||
14) "persons:lastname:stark" <5>
|
||||
15) "persons:address.city:winterfell" <6>
|
||||
```
|
||||
```
|
||||
<1> SET holding all ids known in the keyspace "persons".
|
||||
<2> HASH holding property values for id "9b0ed8ee-14be-46ec-b5fa-79570aadb91d" in keyspace "persons".
|
||||
<3> SET holding index information for CRUD operations.
|
||||
<4> SET used for indexing entities with "firstname" equal to "eddard" within keyspace "persons".
|
||||
<5> SET used for indexing entities with "lastname" equal to "stark" within keyspace "persons".
|
||||
<6> SET used for indexing entities with "address.city" equal to "winterfell" within keyspace "persons".
|
||||
```
|
||||
|
||||
## Configuration ##
|
||||
|
||||
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.
|
||||
|
||||
```java
|
||||
@Configuration
|
||||
@EnableRedisRepositories
|
||||
class AppConfig {
|
||||
|
||||
@Bean
|
||||
RedisConnectionFactory connectionFactory() {
|
||||
return new JedisConnectionFactory();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
|
||||
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
return template;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Having the infrastructure in place you can go on declaring and using the `Repository` interface.
|
||||
|
||||
```java
|
||||
interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
List<Person> findByLastname(String lastname);
|
||||
|
||||
Page<Person> findByLastname(String lastname, Pageable page);
|
||||
|
||||
List<Person> findByFirstnameAndLastname(String firstname, String lastname);
|
||||
|
||||
List<Person> findByFirstnameOrLastname(String firstname, String lastname);
|
||||
|
||||
List<Person> findByAddress_City(String city);
|
||||
}
|
||||
```
|
||||
|
||||
## One Word of Caution ##
|
||||
|
||||
Maintaining complex types and index structures stands and falls with its usage. Please consider the following:
|
||||
|
||||
* Nested document structures increase object <> hash conversion overhead.
|
||||
* Consider having only those indexes you really need instead of indexing each and every property.
|
||||
* Pagination is a costly operation since the total number of elements is calculated before returning just a slice of data.
|
||||
* Pagination gives no guarantee on information as elements might be added, moved or removed.
|
||||
50
redis/repository/pom.xml
Normal file
50
redis/repository/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-data-redis-repository-example</artifactId>
|
||||
<name>Spring Data Redis - Repository Support Example</name>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.data.examples</groupId>
|
||||
<artifactId>spring-data-redis-examples</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-data-redis-example-utils</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.kstyrc</groupId>
|
||||
<artifactId>embedded-redis</artifactId>
|
||||
<version>0.6</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-redis</artifactId>
|
||||
<version>1.7.0.RC1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-keyvalue</artifactId>
|
||||
<version>1.1.0.RC1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-commons</artifactId>
|
||||
<version>1.12.0.RC1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.redis.domain;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import org.springframework.data.redis.core.index.Indexed;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode
|
||||
class Address {
|
||||
|
||||
private @Indexed String city;
|
||||
private String country;
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.redis;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Configuration
|
||||
@EnableRedisRepositories
|
||||
public class AppConfig {
|
||||
|
||||
/**
|
||||
* {@link RedisConnectionFactory} to be used when connecting to redis.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
RedisConnectionFactory connectionFactory() {
|
||||
return new JedisConnectionFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param connectionFactory
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
RedisTemplate<?, ?> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
|
||||
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.redis.domain;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
enum Gender {
|
||||
FEMALE, MALE
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.redis.domain;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import org.springframework.data.annotation.Id;
|
||||
import org.springframework.data.annotation.Reference;
|
||||
import org.springframework.data.redis.core.RedisHash;
|
||||
import org.springframework.data.redis.core.index.Indexed;
|
||||
|
||||
/**
|
||||
* {@link Person} object stored inside a Redis {@literal HASH}. <br />
|
||||
* <br />
|
||||
* Sample (key = persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d):
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* _class := example.springdata.redis.domain.Person
|
||||
* id := 9b0ed8ee-14be-46ec-b5fa-79570aadb91d
|
||||
* firstname := eddard
|
||||
* lastname := stark
|
||||
* gender := MALE
|
||||
* address.city := winterfell
|
||||
* address.country := the north
|
||||
* children.[0] := persons:41436096-aabe-42fa-bd5a-9a517fbf0260
|
||||
* children.[1] := persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53
|
||||
* children.[2] := persons:440b24c6-ede2-495a-b765-2d8b8d6e3995
|
||||
* children.[3] := persons:85f0c1d1-cef6-40a4-b969-758ebb68dd7b
|
||||
* children.[4] := persons:73cb36e8-add9-4ec0-b5dd-d820e04f44f0
|
||||
* children.[5] := persons:9c2461aa-2ef2-469f-83a2-bd216df8357f
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(exclude = { "children" })
|
||||
@RedisHash("persons")
|
||||
class Person {
|
||||
|
||||
/**
|
||||
* The {@literal id} and {@link RedisHash#toString()} build up the {@literal key} for the Redis {@literal HASH}. <br />
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* {@link RedisHash#value()} + ":" + {@link Person#id}
|
||||
* //eg. persons:9b0ed8ee-14be-46ec-b5fa-79570aadb91d
|
||||
* </code>
|
||||
* </pre>
|
||||
*
|
||||
* <strong>Note:</strong> empty {@literal id} fields are automatically assigned during save operation.
|
||||
*/
|
||||
private @Id String id;
|
||||
|
||||
/**
|
||||
* Using {@link Indexed} marks the property as for indexing which uses Redis {@literal SET} to keep track of
|
||||
* {@literal ids} for objects with matching values. <br />
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* {@link RedisHash#value()} + ":" + {@link Field#getName()} +":" + {@link Field#get(Object)}
|
||||
* //eg. persons:firstname:eddard
|
||||
* </code>
|
||||
* </pre>
|
||||
*/
|
||||
private @Indexed String firstname;
|
||||
private @Indexed String lastname;
|
||||
|
||||
private Gender gender;
|
||||
|
||||
/**
|
||||
* Since {@link Indexed} is used on {@link Address#getCity()} index structures for {@code persons:address:city} are be
|
||||
* maintained.
|
||||
*/
|
||||
private Address address;
|
||||
|
||||
/**
|
||||
* Using {@link Reference} allows to link to existing objects via their {@literal key}. The values stored in the
|
||||
* objects {@literal HASH} looks like:
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
* children.[0] := persons:41436096-aabe-42fa-bd5a-9a517fbf0260
|
||||
* children.[1] := persons:1973d8e7-fbd4-4f93-abab-a2e3a00b3f53
|
||||
* children.[2] := persons:440b24c6-ede2-495a-b765-2d8b8d6e3995
|
||||
* </code>
|
||||
* </pre>
|
||||
*/
|
||||
private @Reference List<Person> children;
|
||||
|
||||
public Person() {}
|
||||
|
||||
public Person(String firstname, String lastname, Gender gender) {
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.redis.domain;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
interface PersonRepository extends CrudRepository<Person, String> {
|
||||
|
||||
List<Person> findByLastname(String lastname);
|
||||
|
||||
Page<Person> findPersonByLastname(String lastname, Pageable page);
|
||||
|
||||
List<Person> findByFirstnameAndLastname(String firstname, String lastname);
|
||||
|
||||
List<Person> findByFirstnameOrLastname(String firstname, String lastname);
|
||||
|
||||
List<Person> findByAddress_City(String city);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.redis.domain;
|
||||
|
||||
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
|
||||
import static org.hamcrest.core.Is.*;
|
||||
import static org.hamcrest.core.IsCollectionContaining.*;
|
||||
import static org.hamcrest.core.IsNot.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.RuleChain;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.redis.connection.RedisConnection;
|
||||
import org.springframework.data.redis.core.RedisOperations;
|
||||
import org.springframework.data.redis.core.index.Indexed;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import example.springdata.redis.AppConfig;
|
||||
import example.springdata.redis.test.util.EmbeddedRedisServer;
|
||||
import example.springdata.redis.test.util.RequiresRedisServer;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = AppConfig.class)
|
||||
public class PersonRepositoryTests<K, V> {
|
||||
|
||||
/**
|
||||
* We need to have a Redis server instance available. <br />
|
||||
* 1) Start/Stop an embedded instance or reuse an already running local installation <br />
|
||||
* 2) Ignore tests if startup failed and no server running locally.
|
||||
*/
|
||||
public static @ClassRule RuleChain rules = RuleChain.outerRule(
|
||||
EmbeddedRedisServer.runningAt(6379).suppressExceptions()).around(RequiresRedisServer.onLocalhost());
|
||||
|
||||
/** {@link Charset} for String conversion **/
|
||||
private static final Charset CHARSET = Charset.forName("UTF-8");
|
||||
|
||||
@Autowired RedisOperations<K, V> ops;
|
||||
@Autowired PersonRepository repo;
|
||||
|
||||
/*
|
||||
* Set of test users
|
||||
*/
|
||||
Person eddard = new Person("eddard", "stark", Gender.MALE);
|
||||
Person robb = new Person("robb", "stark", Gender.MALE);
|
||||
Person sansa = new Person("sansa", "stark", Gender.FEMALE);
|
||||
Person arya = new Person("arya", "stark", Gender.FEMALE);
|
||||
Person bran = new Person("bran", "stark", Gender.MALE);
|
||||
Person rickon = new Person("rickon", "stark", Gender.MALE);
|
||||
Person jon = new Person("jon", "snow", Gender.MALE);
|
||||
|
||||
@Before
|
||||
@After
|
||||
public void setUp() {
|
||||
|
||||
ops.execute((RedisConnection connection) -> {
|
||||
connection.flushDb();
|
||||
return "OK";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a single entity and verify that a key for the given keyspace/prefix exists. <br />
|
||||
* Print out the hash structure within Redis.
|
||||
*/
|
||||
@Test
|
||||
public void saveSingleEntity() {
|
||||
|
||||
repo.save(eddard);
|
||||
|
||||
assertThat(ops.execute((RedisConnection connection) -> connection.exists(new String("persons:" + eddard.getId())
|
||||
.getBytes(CHARSET))), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find entity by a single {@link Indexed} property value.
|
||||
*/
|
||||
@Test
|
||||
public void findBySingleProperty() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<Person> starks = repo.findByLastname(eddard.getLastname());
|
||||
|
||||
assertThat(starks, containsInAnyOrder(eddard, robb, sansa, arya, bran, rickon));
|
||||
assertThat(starks, not(hasItem(jon)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find entities by multiple {@link Indexed} properties using {@literal AND}.
|
||||
*/
|
||||
@Test
|
||||
public void findByMultipleProperties() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<Person> aryaStark = repo.findByFirstnameAndLastname(arya.getFirstname(), arya.getLastname());
|
||||
|
||||
assertThat(aryaStark, hasItem(arya));
|
||||
assertThat(aryaStark, not(hasItems(eddard, robb, sansa, bran, rickon, jon)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find entities by multiple {@link Indexed} properties using {@literal OR}.
|
||||
*/
|
||||
@Test
|
||||
public void findByMultiplePropertiesUsingOr() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<Person> aryaAndJon = repo.findByFirstnameOrLastname(arya.getFirstname(), jon.getLastname());
|
||||
|
||||
assertThat(aryaAndJon, containsInAnyOrder(arya, jon));
|
||||
assertThat(aryaAndJon, not(hasItems(eddard, robb, sansa, bran, rickon)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find entities in range defined by {@link Pageable}.
|
||||
*/
|
||||
@Test
|
||||
public void findByReturingPage() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
Page<Person> page1 = repo.findPersonByLastname(eddard.getLastname(), new PageRequest(0, 5));
|
||||
|
||||
assertThat(page1.getNumberOfElements(), is(5));
|
||||
assertThat(page1.getTotalElements(), is(6L));
|
||||
|
||||
Page<Person> page2 = repo.findPersonByLastname(eddard.getLastname(), new PageRequest(1, 5));
|
||||
|
||||
assertThat(page2.getNumberOfElements(), is(1));
|
||||
assertThat(page2.getTotalElements(), is(6L));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find entity by a single {@link Indexed} property on an embedded entity.
|
||||
*/
|
||||
@Test
|
||||
public void findByEmbeddedProperty() {
|
||||
|
||||
Address winterfell = new Address();
|
||||
winterfell.setCountry("the north");
|
||||
winterfell.setCity("winterfell");
|
||||
|
||||
eddard.setAddress(winterfell);
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
List<Person> eddardStark = repo.findByAddress_City(winterfell.getCity());
|
||||
|
||||
assertThat(eddardStark, hasItem(eddard));
|
||||
assertThat(eddardStark, not(hasItems(robb, sansa, arya, bran, rickon, jon)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Store references to other entites without embedding all data. <br />
|
||||
* Print out the hash structure within Redis.
|
||||
*/
|
||||
@Test
|
||||
public void useReferencesToStoreDataToOtherObjects() {
|
||||
|
||||
flushTestUsers();
|
||||
|
||||
eddard.setChildren(Arrays.asList(jon, robb, sansa, arya, bran, rickon));
|
||||
|
||||
repo.save(eddard);
|
||||
|
||||
Person laoded = repo.findOne(eddard.getId());
|
||||
assertThat(laoded.getChildren(), hasItems(jon, robb, sansa, arya, bran, rickon));
|
||||
|
||||
/*
|
||||
* Deceased:
|
||||
*
|
||||
* - Robb was killed by Roose Bolton during the Red Wedding.
|
||||
* - Jon was stabbed by brothers or the Night's Watch.
|
||||
*/
|
||||
repo.delete(Arrays.asList(robb, jon));
|
||||
|
||||
laoded = repo.findOne(eddard.getId());
|
||||
assertThat(laoded.getChildren(), hasItems(sansa, arya, bran, rickon));
|
||||
assertThat(laoded.getChildren(), not(hasItems(robb, jon)));
|
||||
}
|
||||
|
||||
private void flushTestUsers() {
|
||||
repo.save(Arrays.asList(eddard, robb, sansa, arya, bran, rickon, jon));
|
||||
}
|
||||
}
|
||||
14
redis/repository/src/test/resources/logback.xml
Normal file
14
redis/repository/src/test/resources/logback.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d %5p %40.40c:%4L - %m%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="warn">
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</configuration>
|
||||
@@ -21,6 +21,12 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.kstyrc</groupId>
|
||||
<artifactId>embedded-redis</artifactId>
|
||||
<version>0.6</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package example.springdata.redis.test.util;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.rules.ExternalResource;
|
||||
|
||||
import redis.embedded.RedisServer;
|
||||
|
||||
/**
|
||||
* JUnit rule implementation to start and shut down an embedded Redis instance.
|
||||
*
|
||||
* @author Christoph Strobl
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class EmbeddedRedisServer extends ExternalResource {
|
||||
|
||||
private static final int DEFAULT_PORT = 6379;
|
||||
private RedisServer server;
|
||||
private int port = DEFAULT_PORT;
|
||||
private boolean suppressExceptions = false;
|
||||
|
||||
public EmbeddedRedisServer() {
|
||||
|
||||
}
|
||||
|
||||
protected EmbeddedRedisServer(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public static EmbeddedRedisServer runningAt(Integer port) {
|
||||
return new EmbeddedRedisServer(port != null ? port : DEFAULT_PORT);
|
||||
}
|
||||
|
||||
public EmbeddedRedisServer suppressExceptions() {
|
||||
this.suppressExceptions = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.rules.ExternalResource#before()
|
||||
*/
|
||||
@Override
|
||||
protected void before() throws IOException {
|
||||
|
||||
try {
|
||||
|
||||
this.server = new RedisServer(this.port);
|
||||
this.server.start();
|
||||
} catch (Exception e) {
|
||||
if (!suppressExceptions) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.junit.rules.ExternalResource#after()
|
||||
*/
|
||||
@Override
|
||||
protected void after() {
|
||||
|
||||
try {
|
||||
this.server.stop();
|
||||
} catch (Exception e) {
|
||||
if (!suppressExceptions) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user