简体   繁体   中英

Spring-data-redis @Cacheable java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject

I am using spring-data-redis for caching data in my spring boot app. I am using Mongo as my primary data source and Redis as a cache. When I hit the API for the first time, it fetches record from Mongo and saves it in Cache, and returns MyObject correctly to the client. But when I hit the API second time, it finds the record in the Cache, and while trying to deserialize that back into MyObject, it. always runs into a cast exception:

java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to MyObject

Here is my Redis Configuration:

public class MyConfiguration {
    @Bean
    public CacheManager cacheManager(RedisTemplate<String, MyObject> redisTemplate) {
        return new RedisCacheManager(redisTemplate);
    }

    @Bean
    public RedisTemplate<String, MyObject> redisTemplate(RedisConnectionFactory connectionFactory, ObjectMapper objectMapper) {
        StringRedisSerializer serializer = new StringRedisSerializer();
        GenericJackson2JsonRedisSerializer hashValueSerializer = new GenericJackson2JsonRedisSerializer(objectMapper);
        RedisTemplate<String, MyObject> redisTemplate = new RedisTemplate<>();
        redisTemplate.setKeySerializer(serializer);
        redisTemplate.setValueSerializer(hashValueSerializer);
        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }
}

Found same problem reported here: Spring-data-redis @Cacheable java.lang.ClassCastException: java.util.HashMap cannot be cast to java.lang.String

I researched for quite some time but have no ideas. Please suggest. Thanks a ton in advance.

It worked for me by simply setting a new GenericJackson2JsonRedisSerializer() with no arguments, in both Key, value serializer and hash key , value serializer

@Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory, ObjectMapper objectMapper) {
        final RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        // value serializer
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        // hash value serializer
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        logger.info("wiring up Redistemplate...");
        return redisTemplate;
    }

Reason

You have customized the objectMapper, and there is no class name in the Redis value. So it can't be deserialized into the real type.

You can check the redis value, there is no "@class": "com.xxxx.xxx.entity.xx" in the redis value.

My solution

@Bean
public RedisCacheConfiguration redisCacheConfiguration(ObjectMapper objectMapper) {
    // Do not change the default object mapper, we need to serialize the class name into the value
    objectMapper = objectMapper.copy();
    objectMapper = objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
    return RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofMinutes(1))
            .disableCachingNullValues()
            .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer(objectMapper)));
}

I use the objectMapper.enableDefaultTyping() after digging into the source code of the GenericJackson2JsonRedisSerializer.class

It works for me to serialize and deserialize any Object. In this example the cache manager is set TTL you can remove this if you want.

@Configuration
@EnableCaching
public class RedisCacheConfig {

  @Value("${spring.redis.host}")
  private String redisHostName;

  @Value("${spring.redis.port}")
  private int redisPort;

  @Bean
  public LettuceConnectionFactory redisConnectionFactory() {
    return new LettuceConnectionFactory(new RedisStandaloneConfiguration(redisHostName, redisPort));
  }

  @Bean
  public RedisTemplate<Object, Object> redisTemplate() {
    RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<Object, Object>();
    redisTemplate.setConnectionFactory(redisConnectionFactory());
    return redisTemplate;
  }

  @Bean
  @Primary
  public RedisCacheManager redisCacheManager(LettuceConnectionFactory lettuceConnectionFactory) {
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().disableCachingNullValues()
      .entryTtl(Duration.ofMinutes(1))
      .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));

    redisCacheConfiguration.usePrefix();

    return RedisCacheManager.RedisCacheManagerBuilder.fromConnectionFactory(lettuceConnectionFactory)
      .cacheDefaults(redisCacheConfiguration).build();

  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM