8. Redis Repositories
使用Redis存储库可以在Redis哈希中无缝转换和存储域对象,应用自定义映射策略并利用二级索引。
Redis存储库至少需要Redis Server 2.8.0版。
8.1. Usage
要访问存储在Redis中的域实体,您可以利用存储库支持,以便相当显着地简化这些实现。
Example 5. Sample Person Entity
@RedisHash("persons")
public class Person {
@Id String id;
String firstname;
String lastname;
Address address;
}
我们在这里有一个非常简单的域对象domain object。请注意,它有一个名为id的属性,其中注明了org.springframework.data.annotation.Id和一个类型为@RedisHash的注解。这两个负责创建用于保存散列的实际密钥。用@Id注释的属性以及那些名为id的属性被视为标识符属性。 那些带有注释的比其他的更受青睐。
现在实际上有一个负责存储storage和检索retrieval的组件,我们需要定义一个存储库接口repository interface。
Example 6. Basic Repository Interface To Persist Person Entities
public interface PersonRepository extends CrudRepository<Person, String> {
}
由于我们的repository 扩展了CrudRepository,它提供了基本的CRUD和查找操作。 我们需要将两者粘合在一起的是Spring配置。
Example 7. JavaConfig for Redis Repositories
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {
@Bean
public RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
@Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
return template;
}
}
鉴于上面的设置,我们可以继续并将PersonRepository注入到组件中。
Example 8. Access to Person Entities
1. 如果当前值为null,则生成一个新的ID,或重用一个id,然后设置id的值,将Key和keyspace:id,还有Person类型的属性一起存储到Redis Hash中。对于这种情况,例如: persons:5d67b7e1-8640-4475-BEEB-c666fab4c0e5。
2. 使用提供的ID检索存储在keyspace:id处的对象。
3. 通过在Person上使用@RedisHash计算keyspace persons中的所有可用实体的总数。
4. 从Redis中删除给定对象的键。
8.2. Object to Hash Mapping
Redis Repository支持持久化Hashes中的对象。 这需要由RedisConverter完成对象到哈希的转换。 默认实现使用Converter将属性值映射到和来自Redis的native byte[]。
基于前面几节中的Person类型,默认映射如下所示:
1. _class属性包含在根级别以及任何嵌套的接口或抽象类型中。
2. 简单的属性值是通过路径被映射的。
3. 复杂类型的属性由它们的点路径映射。
Table 7. Default Mapping Rules
Type | Sample | Mapped Value |
Simple Type | String firstname = "rand"; | firstname = "rand" |
Complex Type | Address adress = new Address("emond’s field"); | address.city = "emond’s field" |
List | List<String> nicknames = asList("dragon reborn", "lews therin"); | nicknames.[0] = "dragon reborn", |
Map | Map<String, String> atts = asMap({"eye-color", "grey"}, {"… | atts.[eye-color] = "grey", |
List | List<Address> addresses = asList(new Address("em… | addresses.[0].city = "emond’s field", |
Map | Map<String, Address> addresses = asMap({"home", new Address("em… | addresses.[home].city = "emond’s field", |
Mapping behavior can be customized by registering the according Converter
in RedisCustomConversions
. Those converters can take care of converting from/to a single byte[]
as well as Map<String,byte[]>
whereas the first one is suitable for eg. converting one complex type to eg. a binary JSON representation that still uses the default mappings hash structure. The second option offers full control over the resulting hash. Writing objects to a Redis hash will delete the content from the hash and re-create the whole hash, so not mapped data will be lost.
映射行为可以通过在RedisCustomConversions中注册相应的Converter来定制。这些转换器可以处理单个字节byte[]以及Map <String,byte[]>的转换,而第一个转换器是合适的,例如,转换一种复杂类型,例如,一个二进制JSON表示仍然使用默认映射哈希结构。 第二个选项提供了对返回的Hash的完全控制。将对象写入Redis Hash将从哈希中删除内容并重新创建整个哈希,因此未映射的数据将丢失。
Example 9. Sample byte[] Converters
@WritingConverter
public class AddressToBytesConverter implements Converter<Address, byte[]> {
private final Jackson2JsonRedisSerializer<Address> serializer;
public AddressToBytesConverter() {
serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
serializer.setObjectMapper(new ObjectMapper());
}
@Override
public byte[] convert(Address value) {
return serializer.serialize(value);
}
}
@ReadingConverter
public class BytesToAddressConverter implements Converter<byte[], Address> {
private final Jackson2JsonRedisSerializer<Address> serializer;
public BytesToAddressConverter() {
serializer = new Jackson2JsonRedisSerializer<Address>(Address.class);
serializer.setObjectMapper(new ObjectMapper());
}
@Override
public Address convert(byte[] value) {
return serializer.deserialize(value);
}
}
使用上述byte[] Converter
产生的例子:
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address = { city : "emond's field", country : "andor" }
Example 10. Sample Map<String,byte[]> Converters
@WritingConverter
public class AddressToMapConverter implements Converter<Address, Map<String,byte[]>> {
@Override
public Map<String,byte[]> convert(Address source) {
return singletonMap("ciudad", source.getCity().getBytes());
}
}
@ReadingConverter
public class MapToAddressConverter implements Converter<Address, Map<String, byte[]>> {
@Override
public Address convert(Map<String,byte[]> source) {
return new Address(new String(source.get("ciudad")));
}
}
用上述的Map Converter
产生的例子:
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
ciudad = "emond's field"
自定义转换对索引的定义没有任何影响。 即使对于自定义转换类型,二级索引也将会被创建。
8.3. Keyspaces
Keyspaces define prefixes used to create the actual
key
for the Redis Hash. By default the prefix is set to
getClass().getName()
. This default can be altered via
@RedisHash
on aggregate root level or by setting up a programmatic configuration. However, the annotated keyspace supersedes any other configuration.
Keyspaces定义了用于为Redis Hash创建实际Key的前缀。 默认情况下,前缀设置为getClass().getName()。 这个默认值可以通过在类上使用@RedisHash或通过设置程序的配置来改变。 但是,带注解的keyspace将取代任何其他配置。
Example 11. Keyspace Setup via @EnableRedisRepositories
@Configuration
@EnableRedisRepositories(keyspaceConfiguration = MyKeyspaceConfiguration.class)
public class ApplicationConfig {
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
@Override
protected Iterable<KeyspaceSettings> initialConfiguration() {
return Collections.singleton(new KeyspaceSettings(Person.class, "persons"));
}
}
}
Example 12. Programmatic Keyspace setup
@Configuration
@EnableRedisRepositories
public class ApplicationConfig {
//... RedisConnectionFactory and RedisTemplate Bean definitions omitted
@Bean
public RedisMappingContext keyValueMappingContext() {
return new RedisMappingContext(
new MappingConfiguration(
new MyKeyspaceConfiguration(), new IndexConfiguration()));
}
public static class MyKeyspaceConfiguration extends KeyspaceConfiguration {
@Override
protected Iterable<KeyspaceSettings> initialConfiguration() {
return Collections.singleton(new KeyspaceSettings(Person.class, "persons"));
}
}
}