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

RedisInsight2 中文设置_springboot

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类型,默认映射如下所示:

RedisInsight2 中文设置_redis_02

1. _class属性包含在根级别以及任何嵌套的接口或抽象类型中。
2. 简单的属性值是通过路径被映射的。
3. 复杂类型的属性由它们的点路径映射。

Table 7. Default Mapping Rules

Type

Sample

Mapped Value

Simple Type
(eg. String)

String firstname = "rand";

firstname = "rand"

Complex Type
(eg. Address)

Address adress = new Address("emond’s field");

address.city = "emond’s field"

List
of Simple Type

List<String> nicknames = asList("dragon reborn", "lews therin");

nicknames.[0] = "dragon reborn",
nicknames.[1] = "lews therin"

Map
of Simple Type

Map<String, String> atts = asMap({"eye-color", "grey"}, {"…

atts.[eye-color] = "grey",
atts.[hair-color] = "…

List
of Complex Type

List<Address> addresses = asList(new Address("em…

addresses.[0].city = "emond’s field",
addresses.[1].city = "…

Map
of Complex Type

Map<String, Address> addresses = asMap({"home", new Address("em…

addresses.[home].city = "emond’s field",
addresses.[work].city = "…

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"));
    }
  }
}