Redis从基础命令到实战之散列类型(Hash)
从上一篇的实例中可以看出,用字符串类型存储对象有一些不足,在存储/读取时需要进行序列化/反序列化,即时只想修改一项内容,如价格,也必须修改整个键值。不仅增大开发的复杂度,也增加了不必要的性能开销。
一个更好的选择是使用散列类型,或称为Hash表。散列类型与Java中的HashMap相似,是一组键值对的集合,且支持单独对其中一个键进行增删改查操作。使用散列类型存储前面示例中的商品对象,结构如下图所示:
下面先通过示例代码来看散列类型常用的操作命令
一、常用命令
HashExample.java
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set; import redis.clients.jedis.Jedis; public class HashExample { public static void main(String[] args) {
Jedis jedis = JedisProvider.getJedis();
jedis.flushDB(); // 为了避免混淆,下文中对Hash表中的键统称为field String key = "goods"; // hset 仅当操作在hash中创建新field时返回1
Long hset = jedis.hset(key, "id", "1");
print("hset id 1=" + hset + "; value=" + jedis.hget(key, "id")); // 如果field已存在则执行修改,并返回0
hset = jedis.hset(key, "id", "2");
print("hset id 2=" + hset + "; value=" + jedis.hget(key, "id")); // hexists 判断field是否存在
boolean hexists = jedis.hexists(key, "id");
print("hexists id=" + hexists);
hexists = jedis.hexists(key, "title");
print("hexists title=" + hexists); // hsetex 如果field不存在则添加, 已存在则不会修改值, 可用来添加要求不重复的field
Long hsetnx = jedis.hsetnx(key, "id", "3");
print("hsetnx id 3=" + hsetnx + "; value=" + jedis.hget(key, "id"));
hsetnx = jedis.hsetnx(key, "title", "商品001");
print("hsetnx title 商品001=" + hsetnx + "; value=" + jedis.hget(key, "title")); // hmset 设置多个field
Map<String, String> msets = new HashMap<>();
msets.put("color", "red");
msets.put("width", "100");
msets.put("height", "80");
String hmset = jedis.hmset(key, msets);
print("hmset color,width,height=" + hmset); // hincr 新增整数类型的键值对或增加值
long hincr = jedis.hincrBy(key, "price", 4l);
print("hincrBy price 4=" + hincr + "; value=" + jedis.hget(key, "price")); // hlen 读取field数量
print("hlen=" + jedis.hlen(key)); // hkeys 读取所有field
Set<String> sets = jedis.hkeys(key);
print("hkeys=" + Arrays.toString(sets.toArray())); // hvals 读取所有值
List<String> list = jedis.hvals(key);
print("hvals=" + Arrays.toString(list.toArray())); // hgetAll 读取所有键值对
System.out.println("hgetAll 读取所有键值对");
Map<String, String> maps = jedis.hgetAll(key);
for (String field : maps.keySet()) {
System.out.println("hget " + field + "=" + maps.get(field));
}
System.out.println("------------------------------------------------------");
System.out.println(); // hdel 删除field
Long hdel = jedis.hdel(key, "id");
print("hdel id=" + hdel); // 删除多个field
hdel = jedis.hdel(key, "color", "width", "height");
print("hdel color,width,height=" + hdel); // hincrBy 在整数类型值上增加, 返回修改后的值
Long hincrBy = jedis.hincrBy(key, "price", 100l);
print("hincrBy price 100=" + hincrBy); // hget 读取单个field的值
String hget = jedis.hget(key, "title");
print("hget title=" + hget); // hmget 批量读取field的值
jedis.hmget(key, "title", "price");
list = jedis.hvals(key);
print("hmget title,price=" + Arrays.toString(list.toArray())); jedis.close();
} private static void print(String info) {
System.out.println(info);
System.out.println("------------------------------------------------------");
System.out.println();
} }
二、实践练习
对前一篇基于字符串类型的商品管理示例改造,以散列类型存储商品,并增加单独修改标题和修改价格的接口。
首先是添加商品代码
/**
* 添加一个商品
* @param goods
* @return
*/
public boolean addGoods(Goods goods) {
long id = getIncrementId();
Map<String, String> map = new HashMap<>();
map.put("id", String.valueOf(id));
map.put("title", goods.getTitle());
map.put("price", String.valueOf(goods.getPrice()));
String key = "goods:" + id;
return jedis.hmset(key, map).equals("OK");
}
然后增加两个单独修改属性的方法
/**
* 修改商品标题
* @param goods
* @return
*/
public boolean updateTitle(long id, String title) {
String key = "goods:" + id;
return jedis.hset(key, "title", title) == 0;
} /**
* 修改商品价格
* @param id
* @param price
* @return
*/
public boolean updatePrice(long id, float price) {
String key = "goods:" + id;
return jedis.hset(key, "price", String.valueOf(price)) == 0;
}
最后还需要修改读取商品列表的方法
/**
* 读取用于分页的商品列表
* @param pageIndex 页数
* @param pageSize 每页显示行数
* @return
*/
public List<Goods> getGoodsList(int pageIndex, int pageSize) {
int totals = (int)getTotalCount();
int from = (pageIndex - 1) * pageSize;
if(from < 0) {
from = 0;
}
else if(from > totals) {
from = (totals / pageSize) * pageSize;
}
int to = from + pageSize;
if(to > totals) {
to = totals;
}
List<Goods> goodsList = new ArrayList<>();
for(int i = from; i < to; i++) {
String key = "goods:" + (i + 1);
Map<String, String> maps = jedis.hgetAll(key);
Goods goods = new Goods();
goods.setId(NumberUtils.toLong(maps.get("id")));
goods.setTitle(maps.get("title"));
goods.setPrice(NumberUtils.toFloat(maps.get("price")));
goodsList.add(goods);
}
return goodsList;
}
测试代码
public static void main(String[] args) {
HashLession hl = new HashLession();
hl.clear(); //添加一批商品
for(int i = 0; i< 41; i++) {
Goods goods = new Goods(0, "goods" + String.format("%05d", i), i);
hl.addGoods(goods);
}
//读取商品总数
System.out.println("商品总数: " + hl.getTotalCount());
//修改商品价格
for(int i = 1; i <= hl.getTotalCount(); i++) {
hl.updatePrice(i, new Random().nextFloat());
}
//分页显示
List<Goods> list = hl.getGoodsList(2, 20);
System.out.println("第二页商品:");
for(Goods goods : list) {
System.out.println(goods);
}
}
到目前为止,此示例仍然不能支持删除商品的功能,这是因为商品总数是以一个自增数字记录的,且关联了新商品key的生成,删除商品后不能直接减小总数,进而影响到分页的计算。一个比较低效的办法遍历数据库并累加符合规则的key总数,但是更好的做法是以链表保存所有存活的id,这将在下一篇介绍。
Redis从基础命令到实战之散列类型(Hash)的更多相关文章
- Redis从基础命令到实战之有序集合类型(SortedSet)
有序集合类型是Redis五种数据类型中最高级的.也是最复杂的类型.有序集合具有集合类型的特性,在其基础上给每个元素关联了一个分值,或称为权重,操作时既可以在添加元素时指定分值,也可以单独修改集合中某一 ...
- Redis数据类型之散列类型hash
在redis中用的最多的就是hash和string类型. 问题 假设有User对象以JSON序列化的形式存储到redis中, User对象有id.username.password.age.name等 ...
- Redis从基础命令到实战之列表类型(List)
经过上一篇基于Redis散列类型的改造后,实战练习中的商品管理已经具备了增加.修改整体.修改部分属性和分页查询功能,但仍然不支持删除商品的功能.这是因为商品总数是以一个自增数字记录的,且关联了新商品k ...
- Redis命令拾遗二(散列类型)
本文版权归博客园和作者吴双共同所有,欢迎转载,转载和爬虫请注明原文地址 :博客园蜗牛NoSql系列地址 http://www.cnblogs.com/tdws/tag/NoSql/ Redis命令拾 ...
- Redis常用命令入门2:散列类型
散列命令 散列类型的键值其实也是一种字典解耦,其存储了字段和字段值的映射,但字段值只能是字符串,不支持其他数据类型,所以说散列类型不能嵌套其他的数据类型.一个散列类型的键可以包含最多2的32次方-1个 ...
- Redis从基础命令到实战之集合类型(Set)
Redis集合类型的基础功能也是存储字符串列表,和列表类型的区别是字符串不能重复且没有顺序.当然,存储元素唯一性也可以通过应用程序保证,单从这一点上并没有体现出对比列表类型的特点. 其实,集合类型的一 ...
- Redis从基础命令到实战之字符串类型
字符串类型是Redis中最基本的数据类型,能存储任何形式的字符串和和二进制数据.本文以代码形式列举常用的操作命令,并在实践部分演示一个简单的商品管理功能,实现了通常使用关系型数据库开发的增改查功能,注 ...
- redis数据类型-散列类型
Redis数据类型 散列类型 Redis是采用字典结构以键值对的形式存储数据的,而散列类型(hash)的键值也是一种字典结构,其存储了字段(field)和字段值的映射,但字段值只能是字符串,不支持其他 ...
- Redis自学笔记:3.3入门-散列类型
3.3散列类型 3.3.1介绍 散列类型不能嵌套其他数据类型,一个散列类型可以包含至多232-1个字段 散列类型适合存储对象:使用对象类别和ID构成键名,使用字段表示对象的数据, 而字段值则存储属性值 ...
随机推荐
- sql查询单个银行账号重复
非一单位多银行账号. 今天成都公司熊娇付款时候单位名称在弹出的网银补录变成1,从开户银行看都是正常的,只是在分子公司集团这边点击修改开户银行保存就提示错误“银行账号不能重复” select * fro ...
- Linux Epoll相关知识
其实在Linux下设计并发网络程序,向来不缺少方法,比如典型的Apache模型(Process Per Connection,简称PPC),TPC(Thread PerConnection)模型,以及 ...
- Linux的一些常用快捷键和基本命令
*******1.在Linux中,只有/能够当盘符,/首先要分配给系统盘所在分区*******2.swap交换分区,相当于Windows下的虚拟内存,用来模拟内存,当内存不够用时,就会使用交换分区.其 ...
- jquery 获取一组元素的选中项 - 函数、jquery获取复选框值、jquery获取单选按钮值
做表单提交时,如果现在还在用form提交,用户体验很差,所以一般使用ajax提交. 其中需要获取每个表单输入元素的值,获取的时候像文本框这些还好说,Jquery提供了 .val() 方法,获取很方便, ...
- tomcat结合nginx使用小结
相信很多人都听过nginx,这个小巧的东西慢慢地在吞食apache和IIS的份额.那究竟它有什么作用呢?可能很多人未必了解. 说到反向代理,可能很多人都听说,但具体什么是反向代理,很多人估计就不清楚了 ...
- [AIR] AIR 应用程序的调用和终止
本节讨论几种对已安装的 Adobe® AIR® 应用程序进行调用的方法,以及关闭运行中的应用程序的选项和注意事项. 注: NativeApplication.InvokeEvent 和 Browser ...
- JNI中C调用Java方法
背景需求 我们需要在JNI的C代码调用Java代码.实现原理:使用JNI提供的反射借口来反射得到Java方法,进行调用. JNI关键方法讲解. 1. 在同一个类中,调用其他方法 JNIEXPORT v ...
- VBA_Excel_教程:字典类型
VBA中的字典类型需要添加Microsoft Scripting Runtime引用,在Tools菜单下添加 Sub testDic() Dim strV As String Dim key As S ...
- 委托、IOC全知道
话说写代码已有数年,曾经花了很多时间,看了很多大牛的文章也是不能参透,日思夜想都没有理解的概念,通过不断的实践与学习,回过头来再看,总算有了一个清晰的理解与认识,也看到一句话说,最好的学习就是把别人教 ...
- CSS3中-webkit-overflow-scrolling: touch 的使用方法详解
-webkit-overflow-scrolling 属性控制元素在移动设备上是否使用滚动回弹效果. auto 使用普通滚动, 当手指从触摸屏上移开,滚动会立即停止. touch 使用具有回弹效果的滚 ...