具有复杂键的 Spring Data Redis 存储库
Posted
技术标签:
【中文标题】具有复杂键的 Spring Data Redis 存储库【英文标题】:SpringData Redis Repository with complex key 【发布时间】:2020-02-24 15:24:54 【问题描述】:我们尝试在我们的项目中使用 Spring Data CrudRepository
来为我们的域对象提供持久性。
首先,我选择 REDIS 作为后端,因为在第一次使用 CrudRepository<ExperimentDomainObject, String>
的实验中,看起来运行起来很容易。
当尝试将它放入我们的生产代码时,事情变得更加复杂,因为在这里我们的域对象不需要使用简单类型作为键,所以存储库是 CrudRepository<TestObject, ObjectId>
。
现在我得到了例外:
没有找到能够从 [...ObjectId] 类型转换为 [byte[]] 类型的转换器
搜索此异常,this answer,这导致我未经教育地尝试使用 RedisTemplate
配置。 (对于我的实验,我使用的是 emdedded-redis)
我的想法是,提供 RedisTemplate<Object, Object>
而不是 RedisTemplate<String, Object>
以允许使用 Jackson2JsonRedisSerializer
也可以作为 keySerializer 完成工作。
仍然,调用testRepository.save(testObject)
失败。
请看我的代码:
为了简洁起见,我有公共字段并省略了导入。如果需要它们(使其成为 MVCE),我将很乐意提供它们。给我留言吧。
依赖:
dependencies
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
implementation group: 'redis.clients', name: "jedis", version: '2.9.0'
implementation group: 'it.ozimov', name: 'embedded-redis', version: '0.7.2'
Redis 配置:
@Configuration
@EnableRedisRepositories
public class RedisConfiguration
@Bean
JedisConnectionFactory jedisConnectionFactory()
return new JedisConnectionFactory();
@Bean
public RedisTemplate<Object, Object> redisTemplate()
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
final RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
template.setDefaultSerializer(jackson2JsonRedisSerializer);
template.setKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setEnableDefaultSerializer(true);
return template;
测试对象
@RedisHash("test")
public class TestObject
@Id public ObjectId testId;
public String value;
public TestObject(ObjectId id, String value)
this.testId = id;
this.value = value; // In experiment this is: "magic"
对象标识
@EqualsAndHashCode
public class ObjectId
public String creator; // In experiment, this is "me"
public String name; // In experiment, this is "fool"
测试库
@Repository
public interface TestRepository extends CrudRepository<TestObject, ObjectId>
EmbeddedRedisConfiguration
@Configuration
public class EmbeddedRedisConfiguration
private final redis.embedded.RedisServer redisServer;
EmbeddedRedisConfiguration(RedisProperties redisProperties)
this.redisServer = new redis.embedded.RedisServer(redisProperties.getPort());
@PostConstruct
public void init()
redisServer.start();
@PreDestroy
public void shutdown()
redisServer.stop();
应用:
@SpringBootApplication
public class ExperimentApplication
public static void main(String[] args)
SpringApplication.run(ExperimentApplication.class, args);
不是想要的答案:
当然,我可能会介绍一些特殊的 ID,它是一种简单的数据类型,例如我使用 jacksons ObjectMapper 手动构建的 JSON 字符串,然后使用 CrudRepository<TestObject, String>
。
同时我也尝试过:
RedisTemplate<String, String>
RedisTemplate<String, Object>
自动装配 RedisTemplate
并设置其默认序列化程序
注册Converter<ObjectId, byte[]>
到
自动连线ConverterRegistry
自动连线的GenericConversionService
但显然它们是错误的。
【问题讨论】:
【参考方案1】:基本上,Redis 存储库在后台使用 RedisKeyValueTemplate
将数据存储为键 (Id) 和值对。所以你对RedisTemplate
的配置除非你直接使用它,否则它不会起作用。
因此,一种方法是直接使用RedistTemplate
,这样的方法对你有用。
@Service
public class TestService
@Autowired
private RedisTemplate redisTemplate;
public void saveIt(TestObject testObject)
ValueOperations<ObjectId, TestObject> values = redisTemplate.opsForValue();
values.set(testObject.testId, testObject);
所以上面的代码将使用您的配置,并使用 Jackson 作为键和值的映射器在 Redis 中生成字符串对。
但如果您想通过CrudRepository
使用Redis 存储库,您需要为ObjectId
创建从String
和byte[]
到byte[]
的读写转换器,并将它们注册为自定义Redis 转换。
所以让我们为ObjectId
创建读写转换器 String
读者
@Component
@ReadingConverter
@Slf4j
public class RedisReadingStringConverter implements Converter<String, ObjectId>
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public ObjectId convert(String source)
try
return objectMapper.readValue(source, ObjectId.class);
catch (IOException e)
log.warn("Error while converting to ObjectId.", e);
throw new IllegalArgumentException("Can not convert to ObjectId");
作家
@Component
@WritingConverter
@Slf4j
public class RedisWritingStringConverter implements Converter<ObjectId, String>
private ObjectMapper objectMapper = new ObjectMapper();
@Override
public String convert(ObjectId source)
try
return objectMapper.writeValueAsString(source);
catch (JsonProcessingException e)
log.warn("Error while converting ObjectId to String.", e);
throw new IllegalArgumentException("Can not convert ObjectId to String");
以及ObjectId的读写转换器 byte[]
作家
@Component
@WritingConverter
public class RedisWritingByteConverter implements Converter<ObjectId, byte[]>
Jackson2JsonRedisSerializer<ObjectId> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(ObjectId.class);
@Override
public byte[] convert(ObjectId source)
return jackson2JsonRedisSerializer.serialize(source);
读者
@Component
@ReadingConverter
public class RedisReadingByteConverter implements Converter<byte[], ObjectId>
Jackson2JsonRedisSerializer<ObjectId> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(ObjectId.class);
@Override
public ObjectId convert(byte[] source)
return jackson2JsonRedisSerializer.deserialize(source);
最后添加 Redis 自定义对话。只需将代码放入RedisConfiguration
@Bean
public RedisCustomConversions redisCustomConversions(RedisReadingByteConverter readingConverter,
RedisWritingByteConverter redisWritingConverter,
RedisWritingStringConverter redisWritingByteConverter,
RedisReadingStringConverter redisReadingByteConverter)
return new RedisCustomConversions(Arrays.asList(readingConverter, redisWritingConverter, redisWritingByteConverter, redisReadingByteConverter));
现在,在创建转换器并将其注册为自定义 Redis 转换器之后,RedisKeyValueTemplate
可以使用它们,您的代码应该可以按预期工作。
【讨论】:
谢谢,这对我来说是一个好的开始。我会做更多的评估,也许要求澄清,然后我会接受。我对赏金的要求已经满足了。 我现在使用GenericConverter
,它使用ObjectMapper
进行与String
和byte[]
之间的各种转换。我只需要将用作键的类列表传递给它。下一步将是通过注释配置这些类。非常感谢,@Babl
干杯,很高兴听到它有帮助:)
嗨@derM,请您详细说明“我只需将用作键的类列表传递给它”,我想为List<Int>
编写客户转换器,也许这会有所帮助我。
请注意,RedisCustomConversions 参数名称不正确(例如RedisWritingStringConverter redisWritingByteConverter
)。也就是说,它不会影响代码行为,因为它使用了这些类型。谢谢你的好答案。以上是关于具有复杂键的 Spring Data Redis 存储库的主要内容,如果未能解决你的问题,请参考以下文章
使用Spring-data-cassandra查询具有复合主键的表
Spring Data Redis Repository 支持不回读嵌入的复杂对象
使用 Spring Data 保存在 Redis 中的值具有奇怪的前缀
Spring data redis-StringRedisTemplate 用法