对象到哈希的映射
Redis Repository 支持将对象持久化为哈希。这需要进行对象到哈希的转换,该转换由 RedisConverter
完成。默认实现使用 Converter
将属性值映射到 Redis 原生的 byte[]
,并进行反向映射。
给定之前章节中的 Person
类型,默认映射如下所示:
_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
_class
属性包含在根级别以及任何嵌套的接口或抽象类型上。简单属性值通过路径映射。
复杂类型的属性通过其点路径映射。
数据映射与类型转换
本节解释了类型如何与哈希表示之间进行映射:
表 1. 默认映射规则
类型 | 示例 | 映射值 |
---|---|---|
简单类型 (例如,String) | String firstname = "rand"; | firstname = "rand" |
字节数组 (byte[] ) | byte[] image = "rand".getBytes(); | image = "rand" |
复杂类型 (例如,Address) | Address address = new Address("emond’s field"); | address.city = "emond’s field" |
简单类型列表 | List<String> nicknames = asList("dragon reborn", "lews therin"); | nicknames.[0] = "dragon reborn", nicknames.[1] = "lews therin" |
简单类型映射 | Map<String, String> atts = asMap({"eye-color", "grey"}, {"… | atts.[eye-color] = "grey", atts.[hair-color] = "… |
复杂类型列表 | List<Address> addresses = asList(new Address("em… | addresses.[0].city = "emond’s field", addresses.[1].city = "… |
复杂类型映射 | Map<String, Address> addresses = asMap({"home", new Address("em… | addresses.[home].city = "emond’s field", addresses.[work].city = "… |
由于采用了扁平化的表示结构,Map 的键需要是简单类型,例如 String
或 Number
。
可以通过在 RedisCustomConversions
中注册相应的 Converter
来自定义映射行为。这些转换器可以处理从和向单个 byte[]
以及 Map<String, byte[]>
的转换。第一种方式适用于(例如)将复杂类型转换为(例如)仍使用默认映射哈希结构的二进制 JSON 表示。第二种方式提供了对结果哈希的完全控制。
将对象写入 Redis 哈希会删除哈希中的内容并重新创建整个哈希,因此未映射的数据将会丢失。
以下示例展示了两个示例字节数组转换器:
示例 1. 示例 byte[] 转换器
@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
会产生类似于以下的输出:
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
address = { city : "emond's field", country : "andor" }
以下示例展示了两个 Map
转换器的示例:
示例 2. 示例 Map<String, byte[]> 转换器
@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")));
}
}
使用前面的 Converter
映射器会生成类似于以下的输出:
_class = org.example.Person
id = e2c7dcee-b8cd-4424-883e-736ce564363e
firstname = rand
lastname = al’thor
ciudad = "emond's field"
自定义转换对索引解析没有影响。即使对于自定义转换的类型,二级索引仍然会被创建。
自定义类型映射
如果你想避免将整个 Java 类名作为类型信息写入,而是希望使用一个键,你可以在持久化的实体类上使用 @TypeAlias
注解。如果你需要进一步自定义映射,可以参考 TypeInformationMapper 接口。该接口的实例可以在 DefaultRedisTypeMapper
中配置,而 DefaultRedisTypeMapper
又可以在 MappingRedisConverter
上进行配置。
以下示例展示了如何为实体定义类型别名:
示例 3. 为实体定义 @TypeAlias
@TypeAlias("pers")
class Person {
}
生成的文档在 _class
字段中包含 pers
作为值。
配置自定义类型映射
以下示例演示了如何在 MappingRedisConverter
中配置自定义的 RedisTypeMapper
:
示例 4. 通过 Spring Java 配置自定义 RedisTypeMapper
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();
}
}