前言
這一篇文章将講述Redis中的hash類型指令,同樣也是通過demo來講述,其他部分這裡就不在贅述了。
項目Github位址:
https://github.com/rainbowda/learnWay/tree/master/learnRedis/case-hash案例
demo功能是通訊錄,整個demo的大緻頁面如下

準備工作
首先定義一個key的字首,已經存儲自增id的key
private static final String CONTACTS_KEY_PREFIX = "contacts:";
private static final String CONTACTS_ID_KEY = "contactsID";
通訊錄相關的key将會以contacts:1、contacts:2、contacts:3的形式存儲
redis操作對象
private RedisTemplate redisTemplate;
//string 指令操作對象
private ValueOperations valueOperations;
//hash 指令操作對象
private HashOperations hashOperatio
疑惑
如果讀者和我一樣是學Java的,剛聽到hash時的第一反應是這個不是一個算法嗎?當時我也是這樣想的。那麼先來看看hash在Redis中的結構,如下圖(圖檔來源于Redis in Action)。
如果圖看不懂的,我再來介紹下。其實Redis中的hash結構就和mysql中的表類似,把key當做表名,一張表中有多個列名(sub-key),每個列有自己的值(value),然後這張表隻能存放一條資料。不過,這裡的hash結構不會像mysql中固定好的,它可以很友善的增加删除列,例如增加sub-key3删除sub-key1.
新增
指令介紹
先來看看hash中關于新增的一些指令
指令 | 用例 | 描述 |
---|---|---|
HSET | HSET key field value | 設定 key 指定的哈希集中指定字段的值。 |
HSETNX | HSETNX key field value | 當field不存在時,才能成功設定值 |
HMSET | HMSET key field value [field value ...] | 設定 指定的哈希集中指定字段的值(多個) 。 |
接下來看看demo中新增的功能,下圖中點選+按鈕,然後在彈出框中填入name和phone屬性,點選送出後整個新增流程結束。
來看看背景的方法
@RequestMapping(value = "/add",method = RequestMethod.POST)
public boolean add(@RequestBody JSONObject contacts){
//擷取自增id
Long contactsId = valueOperations.increment(CONTACTS_ID_KEY, 1);
contacts.put("id",String.valueOf(contactsId));
//json轉map,然後存入redis
hashOperations.putAll(CONTACTS_KEY_PREFIX+contactsId,contacts.getInnerMap());
return true;
}
- 首先是獲得自增id
- 然後将id存入到前端傳過來的json對象中
- 調用hashOperations對象的putAll方法将對象傳入到Redis中。(putAll方法其實是調用了hmset指令,源碼如下)
public void putAll(K key, Map<? extends HK, ? extends HV> m) {
if (!m.isEmpty()) {
byte[] rawKey = this.rawKey(key);
Map<byte[], byte[]> hashes = new LinkedHashMap(m.size());
Iterator var5 = m.entrySet().iterator();
while(var5.hasNext()) {
Entry<? extends HK, ? extends HV> entry = (Entry)var5.next();
hashes.put(this.rawHashKey(entry.getKey()), this.rawHashValue(entry.getValue()));
}
//調用hMSet
this.execute((connection) -> {
connection.hMSet(rawKey, hashes);
return null;
}, true);
}
}
清單查詢
同樣先看看相關的擷取值指令
HGET | HGET key field | 傳回 key 指定的哈希集中該字段所關聯的值 |
HGETALL | HGETALL key | 傳回 key 指定的哈希集中所有的字段和值。 |
HKEYS | HKEYS key | 傳回 key 指定的哈希集中所有字段的名字。 |
HMGET | HMGET key field [field ...] | 傳回 指定的哈希集中指定字段的值。 |
HVALS | HVALS key | 傳回 key 指定的哈希集中所有字段的值。 |
HSCAN | 用于疊代Hash類型中的鍵值對。 |
HGET和HGETALL指令
來看看HGET和HGETALL在redis用戶端和java中是如何操作的
redis用戶端執行的指令如下
hset key field1 "Hi"
hset key field1 "Hello"
hsetnx key field1 "Hello"
hsetnx key field2 " redis"
hget key field1
hgetall key
執行結果如下
下面是java代碼
@Test
public void hGetAll() {
jedis.hset("key", "field1", "Hi");
redisTemplate.opsForHash().put("key", "field1", "Hello");
System.out.println(jedis.hsetnx("key", "field1", "Hello"));
System.out.println(redisTemplate.opsForHash().putIfAbsent("key", "field2", "Hello"));
System.out.println(jedis.hget("key", "field1"));
System.out.println(jedis.hgetAll("key"));
//spring redisTemplate
System.out.println(redisTemplate.opsForHash().get("key", "field1"));
System.out.println(redisTemplate.opsForHash().entries("key"));
}
hset hashKey field1 value1
hset hashKey field2 value2
hkeys hashKey
@Test
public void hKeys() {
jedis.hset("hashKey", "field1", "value1");
jedis.hset("hashKey", "field2", "value2");
System.out.println(jedis.hkeys("hashKey"));
//spring redisTemplate
System.out.println(redisTemplate.opsForHash().keys("hashKey"));
/**
* 注:兩次結果傳回的順序是不一樣的,
* 因為jedis.hkeys傳回的是HashSet(内部使用HashMap)
* redisTemplate.opsForHash().keys傳回的是LinkHashSet(内部使用LinkHashMap)
*/
}
hmset key field1 value1 field2 value2 field3 value3
hvals key
@Test
public void hVals() {
Map<String, String> map = new HashMap<>(3);
map.put("field1", "value1");
map.put("field2", "value2");
map.put("field3", "value3");
jedis.hmset("key", map);
System.out.println(jedis.hvals("key"));
//spring redisTemplate
System.out.println(redisTemplate.opsForHash().values("key"));
}
查詢方法代碼
接着寫個查詢方法,将新增的内容查詢出來
@RequestMapping(value = "/getList",method = RequestMethod.GET)
public List getList(){
List list = new ArrayList();
//擷取聯系人的keys
Set<String> keys = redisTemplate.keys(CONTACTS_KEY_PREFIX+"*");
for (String key: keys) {
Map entries = hashOperations.entries(key);
list.add(entries);
}
return list;
}
這個hash查詢多個會不友善些,步驟如下
- 擷取相關的key
- 循環查找key相關的資料
- 将查詢出來的結果添加到list中,傳回
添加屬性
來看看代碼
@RequestMapping(value = "/addAttr", method = RequestMethod.POST)
public boolean addAttr(@RequestBody JSONObject contacts){
String id = contacts.getString("id");
String fieldName = contacts.getString("fieldName");
String fieldValue = contacts.getString("fieldValue");
hashOperations.put(CONTACTS_KEY_PREFIX+id, fieldName, fieldValue);
return true;
}
其實就是用hset指令進行插入
hset contacts:1 address 北京9527号
删除屬性
HDEL | HDEL key field [field ...] | 從 key 指定的哈希集中移除指定的域 |
hset hDelKey filed1 filedValue1
hdel hDelKey filed1
hdel hDelKey filed1
現在來看看demo中的删除屬性
代碼如下
@RequestMapping(value = "/delAttr", method = RequestMethod.POST)
public boolean delAttr(@RequestBody JSONObject contacts){
String id = contacts.getString("id");
String fieldName = contacts.getString("fieldName");
hashOperations.delete(CONTACTS_KEY_PREFIX+id, fieldName);
return true;
}
其他指令
HEXISTS | HEXISTS key field | 傳回hash裡面field是否存在 |
HINCRBY | HINCRBY key field increment | 增加 指定的哈希集中指定字段的數值 |
HINCRBYFLOAT | HINCRBYFLOAT key field increment | 同上,加的是浮點型 |
HLEN | HLEN key | 指定的哈希集包含的字段的數量。 |
HSTRLEN | HSTRLEN key field | 傳回hash指定field的value的字元串長度 |
建議學習的人最好每個指令都去敲下,加深印象。下面詩句送給你們。
紙上得來終覺淺,絕知此事要躬行。————出自《冬夜讀書示子聿》