Spring-data-redis @Cacheable java.lang.ClassCastException:java.util.LinkedHashMap不能强制转换为MyObject

im9ewurl  于 2023-10-15  发布在  Redis
关注(0)|答案(4)|浏览(317)

我使用spring-data-redis在我的spring Boot 应用程序中缓存数据。我使用Mongo作为我的主要数据源,Redis作为缓存。当我第一次点击API时,它从Mongo获取记录并将其保存在Cache中,并将MyObject正确返回给客户端。但是当我第二次点击API时,它在该高速缓存中找到了记录,并且在试图将其重新存储到MyObject中时,它。总是会遇到强制转换异常:
java.lang.ClassCastException:java.util.LinkedHashMap不能强制转换为MyObject
以下是Redis配置:

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;
    }
}

发现这里报告的相同问题:Spring-data-redis @Cacheable java.lang.ClassCastException:不能将java.util.HashMap强制转换为java.lang.String
我研究了一段时间,但没有任何想法。请建议一下。提前感谢一吨。

rmbxnbpk

rmbxnbpk1#

理由

你已经自定义了objectMapper,并且在Redis值中没有类名。所以它不能被物化为真实的类型。
你可以检查redis的值,没有 "@class”:redis值中的“com.xxxx.xxx.entity.xx”

我的解决方案

@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)));
}

在深入研究GenericJackson2JsonRedisSerializer.class的源代码后,我使用了objectMapper.enableDefaultTyping()

nvbavucw

nvbavucw2#

它对我来说很有效,只需在Key,value serializerhash keyvalue serializer中设置一个新的GenericJackson2JsonRedisSerializer()no参数,

@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;
    }
lyfkaqu1

lyfkaqu13#

天啊,为什么这么难配置?我几乎是手动的。
Java生态系统很好地解决了一些复杂性,但也带来了很多新的问题。
无论如何,我在Sping Boot 3中使用Kotlin,在Feign HTTP客户端接口的方法上使用@Cacheable,并且能够通过以下配置解决这个问题:

private fun redisCacheDefaultConfiguration(ttlInMinutes: Long) = RedisCacheConfiguration
    .defaultCacheConfig()
    .disableCachingNullValues()
    .entryTtl(Duration.ofMinutes(ttlInMinutes))
    .prefixCacheNameWith("cachename:")
    .serializeKeysWith(SerializationPair.fromSerializer(StringRedisSerializer()))
    .serializeValuesWith(SerializationPair.fromSerializer(GenericJackson2JsonRedisSerializer(getObjectMapper())))

private fun getObjectMapper(): ObjectMapper {
    val objectMapper = JacksonConfiguration().defaultObjectMapper().copy()
    var defaultTypeResolver: StdTypeResolverBuilder = ObjectMapper.DefaultTypeResolverBuilder(
        ObjectMapper.DefaultTyping.EVERYTHING,
        objectMapper.polymorphicTypeValidator
    )
    defaultTypeResolver = defaultTypeResolver.init(JsonTypeInfo.Id.CLASS, null)
    defaultTypeResolver = defaultTypeResolver.inclusion(JsonTypeInfo.As.PROPERTY)
    objectMapper.setDefaultTyping(defaultTypeResolver)
    return objectMapper
}

顺便说一下,如果使用no args构造函数,这与GenericJackson2JsonRedisSerializer创建的ObjectMapper相同。我这样做只是因为我的Map器示例化有一些我想使用的其他配置。
Redis上的条目是:

{
  "@class": "com.path.to.my.DataClazzObj",
  "id": ["java.util.UUID", "a1261350-ca26-4588-961c-c79a42990111"]
}

不漂亮,但做的工作。

txu3uszq

txu3uszq4#

它适用于我序列化和非序列化任何对象。在这个例子中,该高速缓存管理器被设置为TTL,如果需要,您可以删除它。

@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();

  }
}

相关问题