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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章