RedisTemplate查询不到redis中的数据问题(序列化)
一.问题描述
存入Redis中的值取出来却为null,问题根本原因就是RedisTemplate和StringRedisTemplate的序列化问题、代码示例:
1 @SpringBootTest 2 class Redis02SpringbootApplicationTests { 3 4 @Autowired 5 private RedisTemplate redisTemplate; 6 @Test 7 void contextLoads() { 8 Object sd = redisTemplate.opsForValue().get("money");//获取redis中key为“money"的值。 9 System.out.println(sd); 10 } 11 }
但是直接连接redis服务器查询,是有值得
这就让我们迷惑了。为什么idea里面取到的值为null,而在redis客户端上面为什么又能显示?
二. 原因分析
redisTemplate 与StringRedisTemplate 区别
区别主要在于他们使用的序列化类。
- RedisTemplate使用的是 JdkSerializationRedisSerializer
- StringRedisTemplate使用的是 StringRedisSerializer
StringRedisTemplate 继承了RedisTemplate,在构造器中,直接设置了序列化方式
1 public StringRedisTemplate() { 2 this.setKeySerializer(RedisSerializer.string()); 3 this.setValueSerializer(RedisSerializer.string()); 4 this.setHashKeySerializer(RedisSerializer.string()); 5 this.setHashValueSerializer(RedisSerializer.string()); 6 }
而使用RedisTemplate使用的序列类在在操作数据的时候,比如说存入数据会将数据先序列化成字节数组。然后在存入Redis数据库,这个时候打开Redis查看的时候,你会看到你的数据不是以可读的形式展现的,而是以字节数组显示
当然从Redis获取数据的时候也会默认将数据当做字节数组转化,当数组是正常形式时
RedisTemplate就无法获取到数据,这个时候获取到的值就是NULL
当Redis当中的数据值是以可读的形式显示出来的时候,只能使用StringRedisTemplate才能获取到里面的数据。所以当你使用RedisTemplate获取不到数据的时候请检查一下是不是Redis里面的数据是可读形式而非字节数组。
使用StringRedisTemplate之后:
1 @SpringBootTest 2 class Redis02SpringbootApplicationTests { 3 4 @Autowired 5 private StringRedisTemplate stringRedisTemplate; 6 @Test 7 void contextLoads() { 8 Object sd = stringRedisTemplate.opsForValue().get("money"); 9 System.out.println(sd); 10 } 11 }
三.总结
1.redisTemplate只能读取字节数组,不能读取字符串形式的。
2.字符串形式的值,只能使用StringRedisTemplate读取。
四.补充
如果Redistemplate设置了值,在redis客户端却获取不到问题,那该怎么办?
首先,我们要明白一点Redistemplate可以保存所有可序列化的类型,是一个庞大的类,下面就是RedisTemplate类,可以看到俩个泛型
1 public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware
因为Template中set值时会先调用序列化器将键和值都序列化为byte字节数组放入redis数据库中,在客户端除非get后的key值是使用同样的序列化器序列化后的值,否则取不到对应的值。
解决:
自定义Template实现序列化
1 import com.fasterxml.jackson.annotation.JsonAutoDetect; 2 import com.fasterxml.jackson.annotation.PropertyAccessor; 3 import com.fasterxml.jackson.databind.ObjectMapper; 4 import org.springframework.context.annotation.Bean; 5 import org.springframework.context.annotation.Configuration; 6 import org.springframework.data.redis.connection.RedisConnectionFactory; 7 import org.springframework.data.redis.core.RedisTemplate; 8 import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 9 import org.springframework.data.redis.serializer.StringRedisSerializer; 10 11 12 13 @Configuration 14 public class RedisConfig{ 15 // 这是写好的一个固定模板,大家在企业中,拿去就可以直接使用! 16 // 自己定义了一个 RedisTemplate 17 @Bean 18 @SuppressWarnings("all") 19 public RedisTemplate<String, Object>redisTemplate(RedisConnectionFactory factory) { 20 // 我们为了自己开发方便,一般直接使用 <String, Object> 21 RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); 22 template.setConnectionFactory(factory); 23 // Json序列化配置 24 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); 25 ObjectMapper om = new ObjectMapper(); 26 om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 27 om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 28 jackson2JsonRedisSerializer.setObjectMapper(om); 29 // String 的序列化 30 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); 31 // key采用String的序列化方式 32 template.setKeySerializer(stringRedisSerializer); 33 // hash的key也采用String的序列化方式 34 template.setHashKeySerializer(stringRedisSerializer); 35 // value序列化方式采用jackson 36 template.setValueSerializer(jackson2JsonRedisSerializer); 37 // hash的value序列化方式采用jackson 38 template.setHashValueSerializer(jackson2JsonRedisSerializer); 39 template.afterPropertiesSet(); 40 return template; 41 }
//StringRedisTemplate默认使用的序列化方式 44 private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { 45 redisTemplate.setKeySerializer(new StringRedisSerializer()); 46 redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); 47 redisTemplate.setHashKeySerializer(new StringRedisSerializer()); 48 redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); 49 redisTemplate.setConnectionFactory(factory); 50 }
52 }
https://blog.csdn.net/xjszsd/article/details/121746176
五.补RedisTemplate和StringRedisTemplate的区别
RedisTemplate和StringRedisTemplate的区别:
1. 两者的关系是StringRedisTemplate继承RedisTemplate。 2. 两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。 3. SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。 StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。 RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
RedisTemplate默认使用的序列类在在操作数据的时候,比如说存入数据会将数据先序列化成字节数组然后在存入Redis数据库,这个时候打开Redis查看的时候,你会看到你的数据不是以可读的形式展现的,而是以字节数组显示,类似下面
当然从Redis获取数据的时候也会默认将数据当做字节数组转化,这都是根据序列化策略来决定的。
而stringredistemplate,默认存入的数据就是原文,因为stringRedistemplate默认使用的是string序列化策略,使用stringredistemplate默认存入数据长这个样:
造成两者差异的原因是因为在初始化时,两者使用的序列化策略不同导致的,翻开源码可以看到,如下:
// 该方法是重写RedisAccessor的方法 RedisAccessor实现了spring的InitializingBean 也就是在启动时会执行该方法 可以看到该方法默认的序列化为JdkSerializationRedisSerializer
可以看到redistemplate在初始化时是无参构造,通过spring的bean加载机制在项目启动时执行afterPropertiesSet来完成序列化设置,如果需要自定义序列化配置,可以自己写一个redistemplate的bean,来完成配置。
stringredistemplate就比较简单了,直接继承了redistemplate,在初始化时默认使用了string序列化,源码如下:
那么就可以得出一个结论,如果你想使用默认的配置来操作redis,则如果操作的数据是字节数组,就是用redistemplate,如果操作的数据是明文,使用stringredistemplate。
当然在项目中真实使用时,一般是自定义redistemplate的bean实例,来设置具体的序列化策略,说白了就是redistemplate通过自定义bean可以实现和stringredistemplate一样的序列化,使用起来更加灵活。
https://blog.csdn.net/weixin_42140580/article/details/85211887