@Cacheable annotation cannot work as expected when deserialize beans with LocalDateTime type property

1.1k views Asked by At

I found that the annotation @Cacheable cannot work when the method returns a Java Bean type, this is the complete description:

  1. I annotated @Cacheable on a method to use spring cache:
@Cacheable(cacheNames="userCache", key="#userId")
public User getUser(long userId){
    return userRepository.getUserById(userId);
}

And the User class like this:

public class User{
    Long userId;
    String username;
    @JsonSerialize(using = LocalDateTimeSerializer.class)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime birthDateTime;
}

As you can see, I annotated the relating Jackson annotations to make Jackson deserialization for LocalDateTime types work, and this is the related dependency in pom.xml:

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.12.5</version>
        </dependency>
  1. After that, I call the @Cacheable method getUser like this:
User user = userCache.getUser(1L);

and there throws an exception:

org.redisson.client.RedisException: Unexpected exception while processing command at org.redisson.command.CommandAsyncService.convertException(CommandAsyncService.java:326) at org.redisson.command.CommandAsyncService.get(CommandAsyncService.java:123) at org.redisson.RedissonObject.get(RedissonObject.java:82) ...blabla Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type java.time.LocalDateTime not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling at [Source: (io.netty.buffer.ByteBufInputStream); line: 1, column: 101] (through reference chain: com.stackoverflow.domain.User["birthDateTime"]) at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67) at com.fasterxml.jackson.databind.DeserializationContext.reportBadDefinition(DeserializationContext.java:1764) at com.fasterxml.jackson.databind.deser.impl.UnsupportedTypeDeserializer.deserialize(UnsupportedTypeDeserializer.java:36) at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:129)

3.Before I use the @Cacheable, there is no problem if I get the User from database straightly. But when I begin to use @Cacheable, it always throws the exception above, no matter if I configured those Jackson deserialization for LocalDateTime. Is @Cacheable cannot work well with Java Bean with LocalDateTime property, or just my configuration of Jackson is wrong?

1

There are 1 answers

0
robi On

I had the same problem. Spring Cache doesn't use the implicit ObjectMapper used by other Spring components.

  1. Include the module, you already did that.
  2. Create a configuration which will override the default Spring Cache Configuration:
@Configuration
@EnableCaching
public class CacheConfiguration {
    @Bean
    public RedisSerializationContext.SerializationPair<Object> serializationPair() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule())
            .activateDefaultTyping(
                objectMapper.getPolymorphicTypeValidator(),
                ObjectMapper.DefaultTyping.EVERYTHING,
                JsonTypeInfo.As.PROPERTY
            );
        return RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));
    }

    @Bean
    public RedisCacheConfiguration redisCacheConfiguration(
        @Value("${cache.default-ttl-in-seconds}") Integer ttl,
        RedisSerializationContext.SerializationPair<Object> serializationPair
    ) {
        return RedisCacheConfiguration.defaultCacheConfig()
            .disableCachingNullValues()
            .entryTtl(Duration.ofSeconds(ttl))
            .serializeValuesWith(serializationPair);
    }
}