Java對象以Hash結(jié)構(gòu)存入Redis詳解
Java對象以Hash結(jié)構(gòu)存入Redis
Redis中Hash存儲結(jié)構(gòu):
Key:{
filed: value,
filed: value,
filed: value,
....
}和Java中的對象非常相似,卻不能按照J(rèn)ava對象的結(jié)構(gòu)直接存儲進Redis的hash中。因為Java對象中的field是可以嵌套的,而Redis的Hash結(jié)構(gòu)不支持嵌套結(jié)構(gòu)。(不允許套娃~)。
有的同學(xué)要問了,那我就是頭鐵,就要把帶嵌套屬性的對象存儲redis的hash中,應(yīng)該怎么辦?
Spring對所有的RedisClient進行了封裝,提供了一個RedisTemplate。那我們看看RedisTemplate中對hash結(jié)構(gòu)的存儲都接受什么參數(shù)?

從圖中可以看出:
- 一次存一個KV
- 一次性將Map中的所有KV都存入
- 如果不存在就存入
一次只存一個KV就不用說了。重點是第二個putAll方法。既然是Map,那我們不就是可以將帶有嵌套屬性的對象轉(zhuǎn)成Map就可以存進Redis了嗎?
但經(jīng)過實踐最終拋出了異常。正如上面已經(jīng)說過的,Redis的Hash結(jié)構(gòu)不允許套娃。而不帶有嵌套屬性的對象轉(zhuǎn)成Map后可以正常存入。
對象轉(zhuǎn)Map可以使用commons的BeanUtils中的toMap方法。
難道真的就沒有解決辦法了嗎?其實Spring已經(jīng)為我們提供了嵌套屬性轉(zhuǎn)Map的3中實現(xiàn)方案,也就是 HashMapper 接口

- BeanUtilsHashMapper:內(nèi)部依賴commons的BeanUtils將對象轉(zhuǎn)Map,但是官方注釋中也說明了這個實現(xiàn)類不支持嵌套屬性。
- ObjectHashMapper:內(nèi)部使用的是RedisMappingConverter將給定的Java對象提供一個平面的映射,即支持嵌套屬性。
- Jackson2HashMapper:內(nèi)部使用的是Jackon的ObjectMapper將對象轉(zhuǎn)換成扁平的Map
- DecoratingStringHashMapper: 沒有什么特殊的,toHash方法實現(xiàn)了裝飾器(增強)設(shè)計模式,對delegate.toHash的結(jié)果Map中的KV都進行String.valueOf()操作。
可以看出 ObjectHashMapper 和 Jackson2HashMapper 的效果相同,用那個都可以。
注意:Jackson2HashMapper在為對象生成Map時,內(nèi)部對ObjectMapper配置了對Date類型的序列化規(guī)則,會將其換成時間戳Long類型。 在執(zhí)行putAll時,內(nèi)部會報ClassCastException Long cast to String,Long無法直接轉(zhuǎn)換為String。此時可以使用DecoratingStringHashMapper對Jackson2HashMapper進行增強。
下面是一部分的示例代碼和截圖
@Autowired
private RedisTemplate redisTemplate;
/**
* 測試Redis存Hash結(jié)構(gòu)數(shù)據(jù)
*/
@Test
void testSaveRedisHash() throws JsonProcessingException {
User user = new User();user.setUserId(111);user.setPassword("psw");user.setName("張三");user.setCreateTime(new Date());
ObjectMapper objectMapper = new ObjectMapper();
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setHashValueSerializer(RedisSerializer.string());
// key, filed, value
redisTemplate.opsForHash().put("testHash", "hashKey", "hashValue");
// 直接寫JSON,并沒有什么卵用
redisTemplate.opsForHash().put("testHash", "user", objectMapper.writeValueAsString(user));
TestRedisHashObject testObj = new TestRedisHashObject(1, "測試時", new Date(), user);
// 依賴第三方commons.beanUtils包
redisTemplate.opsForHash().putAll("BeanUtilsHashMapper實現(xiàn)對象轉(zhuǎn)hash",new BeanUtilsHashMapper<>(TestRedisHashObject.class).toHash(testObj));
// flatmap為true,才開啟json屬性path扁平化。
// jackson2HashMapper中默認(rèn)的ObjectMapper對時間序列化成了long,而putAll方法中對long無法直接強轉(zhuǎn)成string。會發(fā)生ClassCastException
// decoratingStringHashMapper:將map中的所有kv都使用StringValueOf進行了增強
redisTemplate.opsForHash().putAll("Jackson2HashMapper實現(xiàn)對象轉(zhuǎn)hash",new DecoratingStringHashMapper<>(new Jackson2HashMapper(true)).toHash(testObj));
// ObjectHashMapper返回的Map的泛型是Map<byte[], byte[]> 需要自己手動轉(zhuǎn)換。不推薦使用
// decoratingStringHashMapper:String.valueOf方法無法對byte[]進行轉(zhuǎn)成字符串
// redisTemplate.opsForHash().putAll("ObjectHashMapper實現(xiàn)對象轉(zhuǎn)hash",new ObjectHashMapper().toHash(testObj));
}
/**
* 測試Redis取Hash結(jié)構(gòu)數(shù)據(jù)
*/
@Test
void testGetRedisHash(){
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setHashValueSerializer(RedisSerializer.string());
String key = "Jackson2HashMapper實現(xiàn)對象轉(zhuǎn)hash";
Object o = new Jackson2HashMapper(true).fromHash(redisTemplate.opsForHash().entries(key));
TestRedisHashObject testObj = (TestRedisHashObject) o;
System.out.println(testObj);
Long newCount = redisTemplate.opsForHash().increment(key, "count", 10L);
System.out.println("newCount = " + newCount);
o = new Jackson2HashMapper(true).fromHash(redisTemplate.opsForHash().entries(key));
testObj = (TestRedisHashObject) o;
System.out.println(testObj);
Object c = redisTemplate.opsForHash().get(key, "count");
Long count = (Long) c;
System.out.println("getCount = " + count);
}

到此這篇關(guān)于Java對象以Hash結(jié)構(gòu)存入Redis詳解的文章就介紹到這了,更多相關(guān)Java對象存入Redis內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
swagger整合gateway實現(xiàn)文檔集中化過程
本文介紹了如何將Swagger與Spring?Cloud?Gateway整合,實現(xiàn)API文檔的集中化管理,通過Spring?Boot的自動裝配,配置了網(wǎng)關(guān)Swagger資源提供程序,實現(xiàn)了通過gateway路由的方式聚合Swagger文檔,整個過程包括了文件夾結(jié)構(gòu)、自動裝配文件內(nèi)容等詳細(xì)步驟2026-02-02
java定義通用返回結(jié)果類ResultVO使用示例詳解
這篇文章主要為大家介紹了java定義通用返回結(jié)果類ResultVO使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-09-09
SpringBoot整合Redis實現(xiàn)登錄失敗鎖定功能(實例詳解)
本文我們已經(jīng)探討如何利用Redis來實現(xiàn)鎖定賬戶的安全措施,以及通過SpringBoot整合Redis實現(xiàn)了這一功能,感興趣的朋友跟隨小編一起學(xué)習(xí)下吧2024-02-02
Java求素數(shù)和最大公約數(shù)的簡單代碼示例
這篇文章主要介紹了Java求素數(shù)和最大公約數(shù)的簡單代碼示例,其中作者創(chuàng)建的Fraction類可以用來進行各種分?jǐn)?shù)運算,需要的朋友可以參考下2015-09-09
詳解@ConditionalOnMissingBean注解的作用
這篇文章主要介紹了詳解@ConditionalOnMissingBean注解的作用,@ConditionalOnMissingBean,它是修飾bean的一個注解,主要實現(xiàn)的是,當(dāng)你的bean被注冊之后,如果而注冊相同類型的bean,就不會成功,它會保證你的bean只有一個,需要的朋友可以參考下2023-10-10
Spring Boot配置接口WebMvcConfigurer的實現(xiàn)
這篇文章主要介紹了SpringBoot配置接口WebMvcConfigurer的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11
Java編程實現(xiàn)從給定范圍內(nèi)隨機N個不重復(fù)數(shù)生成隨機數(shù)的方法小結(jié)
這篇文章主要介紹了Java編程實現(xiàn)從給定范圍內(nèi)隨機N個不重復(fù)數(shù)生成隨機數(shù)的方法,結(jié)合實例形式較為詳細(xì)的分析了java根據(jù)指定范圍生成不重復(fù)隨機數(shù)的相關(guān)操作技巧,需要的朋友可以參考下2017-04-04

