From daa4d3cf01ad03a82f5017f156b99cf8dfec2ed9 Mon Sep 17 00:00:00 2001 From: John Blum Date: Wed, 16 Aug 2023 16:33:39 -0700 Subject: [PATCH] Provide workaround in Spring Data Redis RedisCustomConversions for java.time.OffsetDateTime and java.time.OffsetTime types. See https://github.com/spring-projects/spring-data-redis/issues/2677. --- ...ffsetDateTimePropertyIntegrationTests.java | 92 ++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/spring-data-redis-experiments/src/test/java/io/vmware/spring/data/redis/tests/repository/RedisRepositoryWithEntityHavingOffsetDateTimePropertyIntegrationTests.java b/spring-data-redis-experiments/src/test/java/io/vmware/spring/data/redis/tests/repository/RedisRepositoryWithEntityHavingOffsetDateTimePropertyIntegrationTests.java index 9499de5..f86a801 100644 --- a/spring-data-redis-experiments/src/test/java/io/vmware/spring/data/redis/tests/repository/RedisRepositoryWithEntityHavingOffsetDateTimePropertyIntegrationTests.java +++ b/spring-data-redis-experiments/src/test/java/io/vmware/spring/data/redis/tests/repository/RedisRepositoryWithEntityHavingOffsetDateTimePropertyIntegrationTests.java @@ -17,19 +17,27 @@ import static org.assertj.core.api.Assertions.assertThat; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; +import java.time.OffsetTime; +import java.util.List; import org.junit.jupiter.api.Test; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.test.autoconfigure.data.redis.DataRedisTest; import org.springframework.context.annotation.Bean; +import org.springframework.core.convert.converter.Converter; import org.springframework.data.annotation.Id; import org.springframework.data.redis.connection.RedisConfiguration; import org.springframework.data.redis.core.RedisHash; +import org.springframework.data.redis.core.convert.RedisCustomConversions; import org.springframework.data.redis.repository.configuration.EnableRedisRepositories; import org.springframework.data.repository.CrudRepository; import org.springframework.lang.NonNull; @@ -55,6 +63,7 @@ * @see org.springframework.test.context.ContextConfiguration * @see io.vmware.spring.data.redis.tests.AbstractRedisIntegrationTests * @see io.vmware.spring.data.redis.tests.repository.RedisRepositoryWithEntityHavingOffsetDateTimePropertyIntegrationTests.RedisTestConfiguration + * @see Jsr310Converters does not contain converters for OffsetTime and OffsetDateTime * @since 0.1.0 */ @Getter @@ -72,8 +81,9 @@ public class RedisRepositoryWithEntityHavingOffsetDateTimePropertyIntegrationTes void userRepositorySaveAndFindSuccessful() { OffsetDateTime lastAccessed = OffsetDateTime.now(); + OffsetTime timeToLive = OffsetTime.now().plusMinutes(5); - User jonDoe = User.as("Jon Doe").lastAccessed(lastAccessed); + User jonDoe = User.as("Jon Doe").lastAccessed(lastAccessed).timeToLive(timeToLive); getUserRepository().save(jonDoe); @@ -83,6 +93,7 @@ void userRepositorySaveAndFindSuccessful() { assertThat(loadedUser).isNotSameAs(jonDoe); assertThat(loadedUser.getName()).isEqualTo("Jon Doe"); assertThat(loadedUser.getLastAccessed()).isEqualTo(lastAccessed); + assertThat(loadedUser.getTimeToLive()).isEqualTo(timeToLive); } @SpringBootConfiguration @@ -94,6 +105,78 @@ static class RedisTestConfiguration { RedisConfiguration redisConfiguration(RedisProperties redisProperties) { return redisStandaloneConfiguration(redisProperties); } + + @Bean + BeanPostProcessor redisCustomConversionsBeanPostProcess() { + + return new BeanPostProcessor() { + + @Override + public Object postProcessBeforeInitialization(@NonNull Object bean, @NonNull String beanName) throws BeansException { + + return bean instanceof RedisCustomConversions + ? new RedisCustomConversions(ApplicationConverters.list()) + : bean; + } + }; + } + } + + static abstract class ApplicationConverters { + + static List list() { + return List.of( + new BytesToOffsetDateTimeConverter(), + new BytesToOffsetTimeConverter(), + new OffsetDateTimeToBytesConverter(), + new OffsetTimeToBytesConverter() + ); + } + + static class StringBasedConverter { + + static final Charset CHARSET = StandardCharsets.UTF_8; + + byte[] fromString(String source) { + return source.getBytes(CHARSET); + } + + String toString(byte[] bytes) { + return new String(bytes, CHARSET); + } + } + + static class BytesToOffsetTimeConverter extends StringBasedConverter implements Converter { + + @Override + public OffsetTime convert(@NonNull byte[] source) { + return OffsetTime.parse(toString(source)); + } + } + + static class BytesToOffsetDateTimeConverter extends StringBasedConverter implements Converter { + + @Override + public OffsetDateTime convert(@NonNull byte[] source) { + return OffsetDateTime.parse(toString(source)); + } + } + + static class OffsetDateTimeToBytesConverter extends StringBasedConverter implements Converter { + + @Override + public byte[] convert(OffsetDateTime source) { + return fromString(source.toString()); + } + } + + static class OffsetTimeToBytesConverter extends StringBasedConverter implements Converter { + + @Override + public byte[] convert(OffsetTime source) { + return fromString(source.toString()); + } + } } @Getter @@ -107,11 +190,18 @@ static class User { private OffsetDateTime lastAccessed; + private OffsetTime timeToLive; + public @NonNull User lastAccessed(@Nullable OffsetDateTime dateTime) { this.lastAccessed = dateTime; return this; } + public @NonNull User timeToLive(@Nullable OffsetTime time) { + this.timeToLive = time; + return this; + } + @Override public String toString() { return getName();