Redis repository: Error while converting to Timestamp
Using Spring data Redis 3.2.5 and repository, Given this structure
@RedisHash("Fruit")
public class Fruit {
@Id
private Integer id;
private String name;
private Timestamp createdAt;
}
And
public interface FruitRedisRepository extends CrudRepository<Fruit, Integer> {}
@Configuration
public class RedisConfig {
@Bean
public RedisConnectionFactory redisConnectionFactory()
{
return new LettuceConnectionFactory("localhost", 16379);
}
}
This test fails and throws exception:
@SpringBootTest
class TestSpringRedisApplicationTests {
@Autowired
FruitRedisRepository fruitRedisRepository;
@Test
void test() {
//creating sample fruit
Fruit fruit = new Fruit(1, "banana", Timestamp.from(Instant.now()));
//fruit is saved into redis with no problem
assertThat(fruitRedisRepository.save(fruit)).isNotNull();
//this should work fine but throws exception because of the timestamp
assertThat(fruitRedisRepository.findById(1)).isNotNull();
}
}
org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [byte[]] to type [java.sql.Timestamp]
at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:294) at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:185) at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:165) at org.springframework.data.redis.core.convert.MappingRedisConverter.fromBytes(MappingRedisConverter.java:998) at org.springframework.data.redis.core.convert.MappingRedisConverter.readProperty(MappingRedisConverter.java:329) at org.springframework.data.redis.core.convert.MappingRedisConverter.lambda$doReadInternal$0(MappingRedisConverter.java:245) at org.springframework.data.mapping.model.BasicPersistentEntity.doWithProperties(BasicPersistentEntity.java:298) at org.springframework.data.redis.core.convert.MappingRedisConverter.doReadInternal(MappingRedisConverter.java:237) at org.springframework.data.redis.core.convert.MappingRedisConverter.read(MappingRedisConverter.java:183) at org.springframework.data.redis.core.convert.MappingRedisConverter.read(MappingRedisConverter.java:114) at org.springframework.data.redis.core.RedisKeyValueAdapter.get(RedisKeyValueAdapter.java:290) at org.springframework.data.keyvalue.core.KeyValueTemplate.lambda$findById$3(KeyValueTemplate.java:241) at org.springframework.data.keyvalue.core.KeyValueTemplate.execute(KeyValueTemplate.java:314) at org.springframework.data.keyvalue.core.KeyValueTemplate.findById(KeyValueTemplate.java:239) at org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository.findById(SimpleKeyValueRepository.java:98) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:277) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158) at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516) at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285) at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:168) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) at jdk.proxy2/jdk.proxy2.$Proxy59.findById(Unknown Source) at ir.co.rrr.testspringredis.TestSpringRedisApplicationTests.test(TestSpringRedisApplicationTests.java:30) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
But the timestamp is saved correctly in redis:
hmget Fruit:1 id name createdAt
- "1"
- "banana"
- "1713704899797"
Have you tried registering a custom Converter<byte[], Timestamp>? We don't support the SQL timestamp as Redis isn't SQL.
I was mistaking that because it did convert Timestamp to byte[], then it should do the inverse conversion as well. (The conversion from Timestamp to byte[] was because of a BinaryConverter from java.util.date to byte[])
By following documentations, I created a Converter<byte[], Timestamp> and registered it using RedisCustomConversions, but still it throws the same exception and I couldn't find out what am I doing wrong here.
Could you give me a hint on this?
@ReadingConverter
@Component
public class CustomReadingTimestampConverter implements Converter<byte[], Timestamp> {
@Override
public Timestamp convert(byte @NonNull [] source) {
return new Timestamp(Long.parseLong(new String(source)));
}
}
@Configuration
public class RedisConfig {
@Bean
public RedisCustomConversions customConversions(CustomReadingTimestampConverter customTimestampConverter){
return new RedisCustomConversions(List.of(customTimestampConverter));
}
You must register a bean with the name redisCustomConversions like this:
@Bean
public RedisCustomConversions redisCustomConversions(CustomReadingTimestampConverter customTimestampConverter){
return new RedisCustomConversions(List.of(customTimestampConverter));
}
Please add to the documentation that the bean must match by name