Object-to-Hash Mapping

Redis 存储库支持将对象持久化到哈希。这需要对象到哈希的转换,这是由 RedisConverter 完成的。默认实现使用 Converter 来将属性值映射到和从 Redis 本机 byte[]

The Redis Repository support persists Objects to Hashes. This requires an Object-to-Hash conversion which is done by a RedisConverter. The default implementation uses Converter for mapping property values to and from Redis native byte[].

给定前一部分内容中的 Person 类型,默认映射如下所示:

Given the Person type from the previous sections, the default mapping looks like the following:

_class = org.example.Person                 1
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand                            2
lastname = al’thor
address.city = emond's field                3
address.country = andor
1 The _class attribute is included on the root level as well as on any nested interface or abstract types.
2 Simple property values are mapped by path.
3 Properties of complex types are mapped by their dot path.

Data Mapping and Type Conversion

此部分解释了类型与哈希表示之间的映射方式:

This section explains how types are mapped to and from a Hash representation:

Table 1. Default Mapping Rules
Type Sample Mapped Value

Simple Type (for example, String)

String firstname = "rand";

firstname = "rand"

Byte array (byte[])

byte[] image = "rand".getBytes();

image = "rand"

Complex Type (for example, Address)

Address address = 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 = "…​

由于扁平表示结构,映射键需要是简单类型,例如 StringNumber

Due to the flat representation structure, Map keys need to be simple types, such as String or Number.

映射行为可以通过在 RedisCustomConversions 中注册相应的 Converter 来自定义。这些转换器可以负责从和到单个 byte[] 以及 Map<String, byte[]> 的转换。第一个选项适用于(例如)将复杂类型转换为(例如)二进制 JSON 表示,该表示仍然使用默认映射哈希结构。第二个选项对结果哈希提供完全控制。

Mapping behavior can be customized by registering the corresponding Converter in RedisCustomConversions. Those converters can take care of converting from and to a single byte[] as well as Map<String, byte[]>. The first one is suitable for (for example) converting a complex type to (for example) a binary JSON representation that still uses the default mappings hash structure. The second option offers full control over the resulting hash.

将对象写入 Redis 哈希将删除哈希中的内容并重新创建整个哈希,因此未映射的数据将丢失。

Writing objects to a Redis hash deletes the content from the hash and re-creates the whole hash, so data that has not been mapped is lost.

下例展示了两个示例字节数组转换器:

The following example shows two sample byte array converters:

Example 1. 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);
  }
}

使用前面的字节数组 Converter 产生的输出类似于以下内容:

Using the preceding byte array Converter produces output similar to the following:

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address = { city : "emond's field", country : "andor" }

下例展示了 Map 转换器的两个示例:

The following example shows two examples of Map converters:

Example 2. 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<Map<String, byte[]>, Address> {

  @Override
  public Address convert(Map<String, byte[]> source) {
    return new Address(new String(source.get("ciudad")));
  }
}

使用前面的 Map Converter 产生的输出类似于以下内容:

Using the preceding Map Converter produces output similar to the following:

_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
ciudad = "emond's field"

自定义转换对索引解析没有任何影响。即使对于自定义转换的类型,“ Secondary Indexes”仍然会被创建。

Custom conversions have no effect on index resolution. Secondary Indexes are still created, even for custom converted types.

Customizing Type Mapping

如果要避免将整个 Java 类名作为类型信息来编写,而更愿意使用键,则可以使用 @TypeAlias 注释在要持久化的实体类中。如果您需要更多地自定义映射,请查看 link:https://docs.spring.io/spring-data/commons/docs/current/api/org/springframework/data/convert/TypeInformationMapper.html[TypeInformationMapper 接口。可以在 DefaultRedisTypeMapper 中配置该接口的实例,而 MappingRedisConverter 中又可以对其进行配置。

If you want to avoid writing the entire Java class name as type information and would rather like to use a key, you can use the @TypeAlias annotation on the entity class being persisted. If you need to customize the mapping even more, look at the TypeInformationMapper interface. An instance of that interface can be configured at the DefaultRedisTypeMapper, which can be configured on MappingRedisConverter.

下例展示了如何为实体定义类型别名:

The following example shows how to define a type alias for an entity:

Example 3. Defining @TypeAlias for an entity
@TypeAlias("pers")
class Person {

}

生成的 _class 字段中包含 pers 作为值。

The resulting document contains pers as the value in a _class field.

Configuring Custom Type Mapping

以下示例演示如何在 MappingRedisConverter 中配置自定义 RedisTypeMapper

The following example demonstrates how to configure a custom RedisTypeMapper in MappingRedisConverter:

Example 4. Configuring a custom RedisTypeMapper via Spring Java Config
class CustomRedisTypeMapper extends DefaultRedisTypeMapper {
  //implement custom type mapping here
}
@Configuration
class SampleRedisConfiguration {

  @Bean
  public MappingRedisConverter redisConverter(RedisMappingContext mappingContext,
        RedisCustomConversions customConversions, ReferenceResolver referenceResolver) {

    MappingRedisConverter mappingRedisConverter = new MappingRedisConverter(mappingContext, null, referenceResolver,
            customTypeMapper());

    mappingRedisConverter.setCustomConversions(customConversions);

    return mappingRedisConverter;
  }

  @Bean
  public RedisTypeMapper customTypeMapper() {
    return new CustomRedisTypeMapper();
  }
}