一.問題描述
存入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