Redis数据转换ClassCastException和redis Cannot deserialize异常

925 阅读3分钟

这是我参与11月更文挑战的第8天,活动详情查看:2021最后一次更文挑战

今天缓存使用redis hash存储数据,结果在另一个项目里解析REDIS缓存数据报错ClassCastException和redis Cannot deserialize;提交之前特意错了测试,在同项目解析是没有任何问题的,为何别人在其他工程调用失败?百思不得其解。

为什么么使用redis,首先考虑的便是速度,提高同步数据的效率,提前把所需要数据封装好丢进缓存;其次,考虑到分布式环境,如果存放内存可能存在问题。

环境:同项目没有任何问题,不同项目死活解析不出来。

代码:

配置

@Bean
@Qualifier("limitRedisTemplate")
public RedisTemplate<String, Serializable> limitRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {
    RedisTemplate<String, Serializable> template = new RedisTemplate<String, Serializable>();
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

存redis

@Resource
private RedisTemplate<String, Serializable> redisTemplate;

Map<String, ClassType> map数据结构

//数据放入redis
redisTemplate.opsForHash().putAll(RedisConstant.REDIS_KEY_SPECIAL_LINE, map);
Map<Object, Object> entries = redisTemplate.opsForHash().entries(RedisConstant.REDIS_KEY_SPECIAL_LINE);

另一个项目取数据:

配置:

@Bean("limitRedisTemplate")
public RedisTemplate<String, Serializable> limitRedisTemplate(LettuceConnectionFactory redisConnectionFactory) {
    RedisTemplate<String, Serializable> template = new RedisTemplate<String, Serializable>();
    template.setKeySerializer(new StringRedisSerializer());
    template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
    template.setConnectionFactory(redisConnectionFactory);
    return template;
}

取数据:

@Resource
private RedisTemplate<String, String> redisTemplate;
public String getData(String reqClassId){
    Object mapValue = redisTemplate.opsForHash().get("redis_xxxxer_spxxxxne", reqClassId);
    if(!Objects.isNull(mapValue)){
        ClassType m = (ClassType) mapValue;
        return m.getTypeName();
    }
    return "其它";
}

报错位置: Object mapValue = redisTemplate.opsForHash().get("redis_xxxxer_spxxxxne", reqClassId);

错误分析

StringRedisTemplate和RedisTemplate的区别及使用方法,使用时只需使用maven依赖包spring-boot-starter-data-redis即可,然后在service中注入StringRedisTemplate或者RedisTemplate即可。

StringRedisTemplate继承了RedisTemplate,所以两者对Redis的操作方法具有相同之处;

不同之处

  • 两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据;

  • 两者之间的区别主要在于他们使用的序列化类:  RedisTemplate使用的是JdkSerializationRedisSerializer存入数据会将数据先序列化成字节数组然后在存入Redis数据库。  StringRedisTemplate使用的是StringRedisSerializer

使用时注意事项:

   当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可。

   但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择。

RedisTemplate使用时常见问题:

   redisTemplate 中存取数据都是字节数组,比如说存入数据会将数据先序列化成字节数组,然后在存入Redis数据库,这个时候打开Redis查看的时候,你会看到你的数据不是以可读的形式展现的,而是以字节数组显示;当需要获取的数据不是以字节数组存在redis当中,而是正常的可读的字符串的时候,使用redisTemplate取值的时候会无法获取导出数据,获得的值为null。可以使用 StringRedisTemplate 试试。

Cannot deserialize异常错误提示找不到类,反序列化的时候报错,而对象系列化时是跟对象所处的包路径相关的。几个系统用的同一个redis,对象在一个系统中序列化存入redis,包路径com.xxx.tpam.dao.model.Dict;主要原因是因为key也是相同,在运行另一个系统时,发现这个key已经存在了,取出时,因当前这个系统包路径为com.xxx.book.dao.model.Dict,由于两个对象的类所处的包路径不一致,反序列化失败,因而报找不到类错误。

结论

redis中缓存的对象需要实现 Serializable 序列化接口

定制我们的序列化工具

必须重写序列化器,否则@Cacheable注解的value会报类型转换错误 public class StringRedisSerializer implements RedisSerializer

// RedisSerializer<String>redisSerializer = new
// StringRedisSerializer();//Long类型不可以会出现异常信息;
// redisTemplate.setKeySerializer(RedisSerializer.string());
// redisTemplate.setHashKeySerializer(redisSerializer);

字符串数据使用StringRedisTemplate;是复杂的对象类型使用RedisTemplate;

附录:

StringRedisTemplate对于Redis的操作方法:

    StringRedisTemplate.opsForValue().* //操作String字符串类型
    StringRedisTemplate.delete(key/collection) //根据key/keys删除3 
    StringRedisTemplate.opsForList().*  //操作List类型4 
    StringRedisTemplate.opsForHash().*  //操作Hash类型5 
    StringRedisTemplate.opsForSet().*  //操作set类型6 
    StringRedisTemplate.opsForZSet().*  //操作有序set