RedisTemplate查詢不到redis中的數據問題(序列化)
一.問題描述
存入Redis中的值取出來卻為null,問題根本原因就是RedisTemplate和StringRedisTemplate的序列化問題、代碼示例:
@SpringBootTest
class Redis02SpringbootApplicationTests {@Autowiredprivate RedisTemplate redisTemplate;@Testvoid contextLoads() {Object sd = redisTemplate.opsForValue().get("money");//獲取redis中key為“money"的值。System.out.println(sd);}
}
執行結果: idea斷點看無數據,但是直接連接redis服務器查詢,是有值得
二. 原因分析
redisTemplate 與StringRedisTemplate 區別
區別主要在于他們使用的序列化類。
RedisTemplate使用的是 JdkSerializationRedisSerializer
StringRedisTemplate使用的是 StringRedisSerializer
StringRedisTemplate 繼承了RedisTemplate,在構造器中,直接設置了序列化方式
當然從Redis獲取數據的時候也會默認將數據當做字節數組轉化,當數組是正常形式時
RedisTemplate就無法獲取到數據,這個時候獲取到的值就是NULL
當Redis當中的數據值是以可讀的形式顯示出來的時候,只能使用StringRedisTemplate才能獲取到里面的數據。所以當你使用RedisTemplate獲取不到數據的時候請檢查一下是不是Redis里面的數據是可讀形式而非字節數組。
使用StringRedisTemplate之后:
@SpringBootTest
class Redis02SpringbootApplicationTests {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Testvoid contextLoads() {Object sd = stringRedisTemplate.opsForValue().get("money");System.out.println(sd);}
}
拿到數據
三.總結
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 }
五.補RedisTemplate和StringRedisTemplate的區別
RedisTemplate和StringRedisTemplate的區別:
復制代碼
- 兩者的關系是StringRedisTemplate繼承RedisTemplate。
- 兩者的數據是不共通的;也就是說StringRedisTemplate只能管理StringRedisTemplate里面的數據,RedisTemplate只能管理RedisTemplate中的數據。
- SDR默認采用的序列化策略有兩種,一種是String的序列化策略,一種是JDK的序列化策略。
StringRedisTemplate默認采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
RedisTemplate默認采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
RedisTemplate默認使用的序列類在在操作數據的時候,比如說存入數據會將數據先序列化成字節數組然后在存入Redis數據庫,這個時候打開Redis查看的時候,你會看到你的數據不是以可讀的形式展現的,而是以字節數組顯示
當然從Redis獲取數據的時候也會默認將數據當做字節數組轉化,這都是根據序列化策略來決定的。
而stringredistemplate,默認存入的數據就是原文,因為stringRedistemplate默認使用的是string序列化策略
造成兩者差異的原因是因為在初始化時,兩者使用的序列化策略不同導致的,翻開源碼可以看到,如下:
// 該方法是重寫RedisAccessor的方法 RedisAccessor實現了spring的InitializingBean 也就是在啟動時會執行該方法 可以看到該方法默認的序列化為JdkSerializationRedisSerializer
可以看到redistemplate在初始化時是無參構造,通過spring的bean加載機制在項目啟動時執行afterPropertiesSet來完成序列化設置,如果需要自定義序列化配置,可以自己寫一個redistemplate的bean,來完成配置。
stringredistemplate就比較簡單了,直接繼承了redistemplate,在初始化時默認使用了string序列化,源碼如下:
那么就可以得出一個結論,如果你想使用默認的配置來操作redis,則如果操作的數據是字節數組,就是用redistemplate,如果操作的數據是明文,使用stringredistemplate。
當然在項目中真實使用時,一般是自定義redistemplate的bean實例,來設置具體的序列化策略,說白了就是redistemplate通過自定義bean可以實現和stringredistemplate一樣的序列化,使用起來更加靈活。