目录
一、Redis入门
1. Redis简介
1 NoSQL介绍
2 Redis简介
2. Redis下载与安装
1 Redis下载
2 Redis安装
3. Redis服务启动与停止
1 服务启动命令
2 客户端连接命令
3 修改Redis配置文件
3 修改Redis配置文件
4 Redis客户端图形工具
二、Redis数据类型【面试题】
1. 五种常用数据类型介绍
2. 各种数据类型特点
3.Redis的数据类型及应用场景
三、Redis常用命令
1. 字符串操作命令
2. 哈希操作命令
3. 列表操作命令
4. 集合操作命令
5. 有序集合操作命令
6. 通用命令
7. 小结
四、在Java中操作Redis
1. Redis的Java客户端
2. Spring Data Redis使用方式
1 介绍
2 使用入门
1 导入依赖(已完成)
2 配置Redis连接信息
3 使用StringRedisTemplate操作Redis
3 操作常见类型数据
API方法:
操作字符串
操作hash
操作list
操作set
操作zset
通用操作
五、店铺营业状态设置
1. 需求分析和设计
1 产品原型
2 接口设计
3 营业状态存储方式
2. 代码开发
1 常量类BusinessStatusConstant
2 管理端-设置营业状态
2 管理端-查询营业状态
3 用户端-查询营业状态
3. 功能测试
1 接口文档测试
2 接口分组展示
3 前后端联调测试
一、Redis入门
1. Redis简介
1 NoSQL介绍
NoSql(Not Only SQL),不仅仅是SQL,泛指非关系型数据库。NoSql数据库并不是要取代关系型数据库,而是关系型数据库的补充。
关系型数据库(RDBMS): Relational Database Management System。全都是以表的形式存储数据,以约束维护数据关系,有事务
-
数据、操作都非常的严谨,不容易出错
-
三高问题:
高并发问题:关系型数据库如果要提供高并发的能力,需要的代价比较大。
高性能问题:快速从海量数据里找到并操作某些数据,成本高昂
高扩展性问题:集群+分布式,数据库增加、减少节点,或者做数据的迁移 都非常麻烦
-
比如:Mysql,Oracle,DB2,SQLServer
非关系型数据库(NoSql):泛指一切不以表形式存储数据、不使用约束维护关系的数据库
-
特点:更大的优势是在于“灵活”,而不是“严谨”
-
存储数据的模式非常灵活。不以表的形式存储,以什么形式存储的?有各种各样不同的数据库,采用不同的存储形式
-
扩展性和操作性能非常好。这些数据库都是在 大数据量、高并发的情况下,逐渐产生的一些解决方案数据库
-
非关系型数据库,通常缺乏有效的事务管控。
-
-
例如:
-
Redis:键值对结构的数据库。可以把Redis看成一个超级大的独立的HashMap。性能极强
-
MongoDB:文档型数据库。每一条数据存储成一个json对象
-
HBase:列式数据库。可以很方便的存储海量的数据
-
Neo4J:图数据库。更适合于维护拓扑结构的关系,比如社交关系
-
2 Redis简介
官网:Redis, 中文网:Redis中文网
Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库,而且为了进一步提升性能,Redis把数据存储到内存里,所以它有极高的读写性能。
官方提供的数据是可以达到100000+的QPS(每秒内查询次数),是互联网技术领域使用最为广泛的存储中间件。它存储的value类型比较丰富,是NoSql数据库的一种。
主要特点:
-
基于内存存储,读写性能高
-
适合存储热点数据(热点商品、资讯、新闻)
-
企业应用广泛
2. Redis下载与安装
1 Redis下载
Redis安装包分为windows版和Linux版:
-
Windows版下载地址:Releases · microsoftarchive/redis · GitHub
-
Linux版下载地址: Index of /releases/
资料中已提供好的安装包:
2 Redis安装
1)在Windows中安装Redis(项目中使用)
Redis的Windows版属于绿色软件,直接解压即可使用,解压后目录结构如下:
2)在Linux中安装Redis(简单了解)
在Linux系统安装Redis步骤:
-
将Redis安装包上传到Linux
-
解压安装包,命令:tar -zxvf redis-4.0.0.tar.gz -C /usr/local
-
安装Redis的依赖环境gcc,命令:yum install gcc-c++
-
进入/usr/local/redis-4.0.0,进行编译,命令:make
-
进入redis的src目录进行安装,命令:make install
安装后重点文件说明:
-
/usr/local/redis-4.0.0/src/redis-server:Redis服务启动脚本
-
/usr/local/redis-4.0.0/src/redis-cli:Redis客户端脚本
-
/usr/local/redis-4.0.0/redis.conf:Redis配置文件
3. Redis服务启动与停止
以window版Redis进行演示:
1 服务启动命令
-
如果要使用默认配置启动:直接双击
redis-server.exe
-
如果要加载配置文件启动:在cmd里
-
切换到redis的目录里
cd /d Redis文件夹的路径
-
执行命令
redis-server.exe redis.windows.conf
-
Redis服务默认端口号为 6379 ,通过快捷键Ctrl + C 即可停止Redis服务
当Redis服务启动成功后,可通过客户端进行连接。
2 客户端连接命令
-
如果要连接本机6379端口的redis:直接双击
redis-cli.exe
-
如果要连接非本机或非6379端口的Redis:在cmd里
-
切换到Redis的目录里
cd /d Redis文件夹的路径
-
执行命令
redis-cli.exe -h ip地址 -p 端口号 -a 密码
-
3 修改Redis配置文件
设置Redis服务密码,修改redis.windows.conf
requirepass 123456
注意:
-
修改密码后需要重启Redis服务才能生效
-
Redis配置文件中 # 表示注释
重启Redis后,再次连接Redis时,需加上密码,否则连接失败。
3 修改Redis配置文件
设置Redis服务密码,修改redis.windows.conf
requirepass 123456
注意:
-
修改密码后需要重启Redis服务才能生效
-
Redis配置文件中 # 表示注释
重启Redis后,再次连接Redis时,需加上密码,否则连接失败。
redis-cli.exe -h localhost -p 6379 -a 123456
此时,-h 和 -p 参数可省略不写。
4 Redis客户端图形工具
默认提供的客户端连接工具界面不太友好,同时操作也较为麻烦,接下来,引入一个Redis客户端图形工具。
上面的压缩包资源,审核中
新建连接
连接成功
二、Redis数据类型【面试题】
1. 五种常用数据类型介绍
Redis存储的是key-value结构的数据,其中key是字符串类型,value有5种常用的数据类型:
-
字符串 string
-
哈希 hash
-
列表 list
-
集合 set
-
有序集合 sorted set / zset
2. 各种数据类型特点
-
字符串(string):普通字符串,Redis中最简单的数据类型
-
哈希(hash):也叫散列,类似于Java中的HashMap结构
-
列表(list):按照插入顺序排序,可以有重复元素,类似于Java中的LinkedList
-
集合(set):无序集合,没有重复元素,类似于Java中的HashSet
-
有序集合(sorted set/zset):集合中每个元素关联一个分数(score),根据分数升序排序,没有重复元素
3.Redis的数据类型及应用场景
Redis支持五种主要的数据类型:字符串(String)、哈希(Hash)、列表(List)、集合(Set)和有序集合(Sorted Set)。每种数据类型都有其特定的使用场景,如下所示:
-
字符串:通常用于存储不需要频繁变动的配置信息或标识符,如用户ID、设备标识等。
-
哈希:适合存储对象信息,例如用户资料、商品信息,它们可以通过key-value的形式快速访问。
-
列表:可以用于存储有序的元素序列,如评论列表、消息队列等。
-
集合:用于存储无序且唯一的元素集,常用于交集、差集运算。
-
有序集合:通过为每个元素关联一个分数,实现按照某种顺序遍历元素,适用于排行榜、时间轴等功能
三、Redis常用命令
更多命令可以参考:
-
参考Redis中文网:Redis中文网
-
参考Redis官网:Commands | Redis
1. 字符串操作命令
Redis 中字符串类型常用命令:
操作示例:
127.0.0.1:6379> set name robin #存储一项数据。key是name,value是robin
OK
127.0.0.1:6379> get name #获取key为name的value值
"robin"
127.0.0.1:6379> del name #删除key为name的数据
(integer) 1
127.0.0.1:6379> get name #获取key为name的值,获取不到,已经被删除掉了
(nil)
127.0.0.1:6379> set verify_code 123456 #设置一项数据,key是verify_code,值是123456
OK
127.0.0.1:6379> get verify_code #获取key为verify_code的值
"123456"
127.0.0.1:6379> setnx verify_code 11111 #setnx设置verify_code的值,因为这个key已经存在,所以设置不成功
(integer) 0
127.0.0.1:6379> get verify_code #重新获取verify_code的值,并没有被修改成11111
"123456"
127.0.0.1:6379> set verify_code 22222 #set设置verify_code的值,会把对应的值给覆盖掉
OK
127.0.0.1:6379> get verify_code #重新获取verify_code的值,得到修改后的22222
"22222"
127.0.0.1:6379> setex code 10 abcde #设置一个key为code,值为abcde,并且指定它的有效期为10s(10s后自动消失)
OK
127.0.0.1:6379> get code #还不到10s,可以获取到
"abcde"
127.0.0.1:6379> get code #还不到10s,可以获取到
"abcde"
127.0.0.1:6379> get code #到10s了,数据已经没有了
(nil)
127.0.0.1:6379>
2. 哈希操作命令
Redis hash 是一个string类型的 field 和 value 的映射表,hash特别适合用于存储对象,常用命令:
127.0.0.1:6379> hset user_1 name chenjinlong #向key为user_1的小hash里,存储了name--chenjinlong键值对
(integer) 1
127.0.0.1:6379> hset user_1 age 23 #向key为user_1的小hash里,存储了age--23键值对
(integer) 1
127.0.0.1:6379> hset user_1 gender girl #向key为user_1的小hash里,存储了gender-girl键值对
(integer) 1
127.0.0.1:6379> hget user_1 name #从key为user_1对应的小hash里,获取name的值
"chenjinlong"
127.0.0.1:6379> hdel user_1 name #从key为user_1对应的小hash里,删除name
(integer) 1
127.0.0.1:6379> hget user_1 name #从key为user_1对应的小hash里,重新获取name的值,已经没有了
(nil)
127.0.0.1:6379> hkeys user_1 #从key为user_1对应的小hash里,获取所有的field
1) "age"
2) "gender"
127.0.0.1:6379> hvals user_1 #从key为user_1对应的小hash里,获取所有的value
1) "23"
2) "girl"
127.0.0.1:6379> hgetall user_1 #从key为user_1对应的小hash里,获取所有的field-value键值对
1) "age"
2) "23"
3) "gender"
4) "girl"
127.0.0.1:6379> hlen user_1 #从key为user_1对应的小hash里,获取小hash的长度
(integer) 2
127.0.0.1:6379>
3. 列表操作命令
Redis 列表是简单的字符串列表,按照插入顺序排序,常用命令:
127.0.0.1:6379> lpush mylist msg1 msg2 msg3 msg4 #向mylist对应的列表里,从左边依次添加msg1,msg2,msg3,msg4
(integer) 4
127.0.0.1:6379> lrange mylist 0 3 #从mylist对应的列表里,查询索引0到索引3的数据(包含头和尾)
1) "msg4"
2) "msg3"
3) "msg2"
4) "msg1"
127.0.0.1:6379> llen mylist #从mylist对应的列表里,查询列表的长度
(integer) 4
127.0.0.1:6379> lrange mylist 0 -1 #从mylist对应的列表里,查询索引0到 倒数第1个值(查询全部)
1) "msg4"
2) "msg3"
3) "msg2"
4) "msg1"
127.0.0.1:6379> lrange mylist 0 -2 #从mylist对应的列表里,查询索引0到 倒数第2个值
1) "msg4"
2) "msg3"
3) "msg2"
127.0.0.1:6379> rpop mylist #从mylist对应的列表里,弹出最右边的值
"msg1"
127.0.0.1:6379> rpop mylist #从mylist对应的列表里,弹出最右边的值
"msg2"
127.0.0.1:6379> rpop mylist #从mylist对应的列表里,弹出最右边的值
"msg3"
127.0.0.1:6379> rpop mylist #从mylist对应的列表里,弹出最右边的值
"msg4"
127.0.0.1:6379> rpop mylist #从mylist对应的列表里,弹出最右边的值。已经全部弹出,队列里空了
(nil)
127.0.0.1:6379> brpop mylist 5 #从mylist对应的列表里,弹出最右边的值。如果没有值,就等待5s
(nil)
(5.06s)
4. 集合操作命令
Redis set 是string类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据,常用命令:
127.0.0.1:6379> sadd myset chenjinlong yangchenchen jiangtao yaoyuanyuan liuchaoze #向myset对应的集合里添加多个成员
(integer) 5
127.0.0.1:6379> smembers myset #查询myset对应的set集合里,所有的成员
1) "chenjinlong"
2) "jiangtao"
3) "yaoyuanyuan"
4) "yangchenchen"
5) "liuchaoze"
127.0.0.1:6379> srandmember myset #从myset对应的set集合里,随机获取一个
"yangchenchen"
127.0.0.1:6379> srandmember myset #从myset对应的set集合里,随机获取一个
"yangchenchen"
127.0.0.1:6379> scard myset #查询myset对应的set集合里,成员的个数
(integer) 5
127.0.0.1:6379> srem myset jiangtao #从myset对应的set集合里,移除掉一个成员jiangtao
(integer) 1
127.0.0.1:6379> smembers myset #重新查询成员,已经少了一个
1) "liuchaoze"
2) "yangchenchen"
3) "chenjinlong"
4) "yaoyuanyuan"
127.0.0.1:6379>
127.0.0.1:6379> sadd myset1 tom jerry jack rose #演示集合运算,准备第1个set集合
(integer) 4
127.0.0.1:6379> sadd myset2 tom jerry robin pony #演示集合运算,准备第2个set集合
(integer) 4
127.0.0.1:6379> sinter myset1 myset2 #计算两个set集合的交集(你有我也有的)
1) "tom"
2) "jerry"
127.0.0.1:6379> sunion myset1 myset2 #计算两个set集合的并集(大家全加起来)
1) "rose"
2) "pony"
3) "jerry"
4) "tom"
5) "jack"
6) "robin"
127.0.0.1:6379> sdiff myset1 myset2 #计算两个set集合的差集:myset1 - myset2
1) "rose"
2) "jack"
127.0.0.1:6379> sdiff myset2 myset1 #计算两个set集合的差集:myset2 - myset1
1) "pony"
2) "robin"
127.0.0.1:6379>
5. 有序集合操作命令
Redis有序集合是string类型元素的集合,且不允许有重复成员。每个元素都会关联一个double类型的分数。
注意事项:
-
zset默认排序是升序的
-
排名rank是从0开始,0表示第1名
常用命令:
127.0.0.1:6379> zadd myzset 30 zhangsan 40 lisi 50 wangwu 60 zhaoliu 70 qianqi #向key为myzset的有序集合里添加成员
(integer) 5
127.0.0.1:6379> zrank myzset lisi #从myzset里查询lisi的名次(从0开始的)
(integer) 1
127.0.0.1:6379> zrank myzset zhangsan #从myzset里查询zhangsan的名次(从0开始的)
(integer) 0
127.0.0.1:6379> zscore myzset zhangsan #从myzset里查询zhangsan的分值
"30"
127.0.0.1:6379> zrange myzset 0 2 #从myzset里从索引0(排名0)查询到索引2(排名2)的成员,包含头尾
1) "zhangsan"
2) "lisi"
3) "wangwu"
127.0.0.1:6379> zrange myzset 0 2 withscores #从myzset里从索引0(排名0)查询到索引2(排名2)成员及分值,包含头尾,
1) "zhangsan"
2) "30"
3) "lisi"
4) "40"
5) "wangwu"
6) "50"
127.0.0.1:6379> zrange myzset 0 -1 withscores #从myzset里从索引0(排名0)查询到索最后1个 成员及分值,包含头尾,
1) "zhangsan"
2) "30"
3) "lisi"
4) "40"
5) "wangwu"
6) "50"
7) "zhaoliu"
8) "60"
9) "qianqi"
10) "70"
127.0.0.1:6379> zincrby myzset 100 zhangsan #给myzset里zhangsan成员的分值,增加上100
"130"
127.0.0.1:6379> zrange myzset 0 -1 withscores #查询所有成员及分值,发现zhangsan的分值和排名已经变了
1) "lisi"
2) "40"
3) "wangwu"
4) "50"
5) "zhaoliu"
6) "60"
7) "qianqi"
8) "70"
9) "zhangsan"
10) "130"
127.0.0.1:6379> zincrby myzset -100 zhangsan #给myzset里zhangsan成员的分值,增加上-100
"30"
127.0.0.1:6379> zrem myzset zhangsan #从myzset里删除掉zhangsan成员
(integer) 1
127.0.0.1:6379> zrange myzset 0 -1 #再查询所有成员,发现zhangsan已经被删除掉了
1) "lisi"
2) "wangwu"
3) "zhaoliu"
4) "qianqi"
127.0.0.1:6379>
6. 通用命令
Redis的通用命令是不分数据类型的,都可以使用的命令:
127.0.0.1:6379> keys * #查询当前Redis实例里的所有的key
1) "myset"
2) "myset1"
3) "verify_code"
4) "myzset"
5) "user_1"
6) "myset2"
127.0.0.1:6379> keys my* #查询当前Redis实例里以my开头的key
1) "myset"
2) "myset1"
3) "myzset"
4) "myset2"
127.0.0.1:6379> keys myset? #查询 myzset + 一个任意字符的key
1) "myset1"
2) "myset2"
127.0.0.1:6379> exists myzset #判断myzset这个key是否存在
(integer) 1
127.0.0.1:6379> exists xxx #判断xxx这个key是否存在
(integer) 0
127.0.0.1:6379> type myset #判断myset的类型
set
127.0.0.1:6379> type myzset #判断myzset的类型
zset
127.0.0.1:6379> del myzset #删除key为myzset的数据
(integer) 1
127.0.0.1:6379> keys * #删除之后再查询,已经没有myzset了
1) "myset"
2) "myset1"
3) "verify_code"
4) "user_1"
5) "myset2"
127.0.0.1:6379>
7. 小结
key的类型:字符串或字节数组
value的类型:常用的有
string:短信验证码
存:set key value
取:get key
删:del key
hash:存储一组相关的数据,比如购物车
存:hset key field value
取:hget key field
删:hdel key field
list:用于排队场景
存:lpush key value1 value2 .... 从左边添加到列表里
取:rpop key 从右边弹出最边缘的这个元素值
set:用于不分名次的一批数据存储,比如 点赞人
存:sadd key value1 value2 ....
取:srandmember key 从key对应的set里,随机取一个
删:srem key value1 value2 ....
zset:用于排行榜
存:zadd key score1 value1 score2 value2 ...
取:
取某成员的rank名次:zrank key value
取某成员的score值: zscore key value
按名次范围取成员: zrange key 开始索引 结束索引 withscores
删:zrem key value1 value2...
四、在Java中操作Redis
1. Redis的Java客户端
前面我们讲解了Redis的常用命令,这些命令是我们操作Redis的基础,那么我们在java程序中应该如何操作Redis呢?这就需要使用Redis的Java客户端,就如同我们使用JDBC操作MySQL数据库一样。
Redis 的 Java 客户端很多,常用的几种:
-
Jedis
-
Lettuce
-
Spring Data Redis
Spring 对 Redis 客户端进行了整合,提供了 Spring Data Redis,在Spring Boot项目中还提供了对应的Starter,即 spring-boot-starter-data-redis。
我们重点学习Spring Data Redis。
2. Spring Data Redis使用方式
1 介绍
Spring Data Redis 是 Spring 的一部分,提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务,对 Redis 底层开发包进行了高度封装。在 Spring 项目中,可以使用Spring Data Redis来简化 Redis 操作。
网址:Spring Data Redis
Spring Boot提供了对应的Starter,maven坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
Spring Data Redis中提供了 用于操作redis的高度封装的类:
-
RedisTemplate:操作Redis的模板类,它提供了操作Redis的一系列方法。
-
StringRedisTemplate:是RedisTemplate的子类,采用String序列化方式
对相关api进行了归类封装,将同一类型操作封装为operation接口,具体分类如下:
-
ValueOperations:string数据操作
-
SetOperations:set类型数据操作
-
ZSetOperations:zset类型数据操作
-
HashOperations:hash类型的数据操作
-
ListOperations:list类型的数据操作
2 使用入门
进入到sky-server模块
1 导入依赖(已完成)
导入Spring Data Redis的maven坐标
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2 配置Redis连接信息
简单配置,直接在application.yml里配置如下:
spring:
redis:
host: localhost
port: 6379
#password:
database: 0 #一个Redis服务默认有16个database子库,我们默认使用的是0号库
了解苍穹里项目的配置,简单了解就行了。按照上边的配置完全是可以的,不必下面这样复杂配置
在application-dev.yml中配置Redis数据源:
sky:
redis:
host: localhost
port: 6379
password: 123456
database: 10
解释说明:
database:指定使用Redis的哪个数据库,Redis服务启动后默认有16个数据库,编号分别是从0到15。
可以通过修改Redis配置文件来指定数据库的数量。
在application.yml中添加读取application-dev.yml中的相关Redis配置
spring:
profiles:
active: dev
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
password: ${sky.redis.password}
database: ${sky.redis.database}
3 使用StringRedisTemplate操作Redis
在test下新建测试类
package com.sky.test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
@SpringBootTest
public class SpringDataRedisTest {
@Autowired
private StringRedisTemplate redisTemplate;
@Test
public void testRedisTemplate(){
System.out.println(redisTemplate);
//string数据操作
ValueOperations valueOperations = redisTemplate.opsForValue();
//hash类型的数据操作
HashOperations hashOperations = redisTemplate.opsForHash();
//list类型的数据操作
ListOperations listOperations = redisTemplate.opsForList();
//set类型数据操作
SetOperations setOperations = redisTemplate.opsForSet();
//zset类型数据操作
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
}
}
如果能打印成功,说明RedisTemplate对象注入成功,并且通过该RedisTemplate对象获取操作5种数据类型相关对象。
3 操作常见类型数据
API方法:
StringRedisTemplate对象提供的常用方法:
通用操作:
-
delete(String key)
:删除key
操作字符串:
-
opsForValue().set(String key, String value)
:存储一个键值对。相当于命令 set key value -
opsForValue().setIfAbsent(String key, String value)
:存储一个键值对。如果key不存在,会存储成功;否则会存储失败
返回值:Boolean,true表示存储成功了,false表示存储失败了
-
opsForValue().get(String key)
:获取key对应的值。相当于命令 get key
操作hash:
-
存:
opsForHash().put(String key, String field, String value)
-
取:
opsForHash().get(String key, String field)
-
删:
opsForHash().delete(String key, String field)
操作list:
-
存:
-
opsForList().leftPush(String key, String value)
-
opsForList().leftPushAll(String key, String... value)
-
-
取:
opsForList().rightPop(String key)
操作set:
-
存:
opsForSet().add(String key, String... value)
-
取:
-
取所有成员:
opsForSet().members(String key)
-
随机取成员:
opsForSet().randomMember(String key)
-
-
删:
opsForSet().remove(String key, String... value)
操作zset:
-
存:
opsForZSet().add(String key, String value, double score)
-
取:
-
取某成员的名次:
opsForZSet().rank(String key, String value)
-
取某成员的分值:
opsForZSet().score(String key, String value)
-
按名次范围查询:
opsForZSet().range(String key, long start, long stop)
-
-
删:
opsForZSet().remove(String key, String... value)
操作字符串
/**
* 操作字符串类型的数据
*/
@Test
public void testString(){
// set get setex setnx
redisTemplate.opsForValue().set("name","小明");
String city = (String) redisTemplate.opsForValue().get("name");
System.out.println(city);
redisTemplate.opsForValue().set("code","1234",3, TimeUnit.MINUTES);
redisTemplate.opsForValue().setIfAbsent("lock","1");
redisTemplate.opsForValue().setIfAbsent("lock","2");
}
操作hash
/**
* 操作哈希类型的数据
*/
@Test
public void testHash(){
//hset hget hdel hkeys hvals
HashOperations hashOperations = redisTemplate.opsForHash();
hashOperations.put("100","name","tom");
hashOperations.put("100","age","20");
String name = (String) hashOperations.get("100", "name");
System.out.println(name);
Set keys = hashOperations.keys("100");
System.out.println(keys);
List values = hashOperations.values("100");
System.out.println(values);
hashOperations.delete("100","age");
}
操作list
/**
* 操作列表类型的数据
*/
@Test
public void testList(){
//lpush lrange rpop llen
ListOperations listOperations = redisTemplate.opsForList();
listOperations.leftPushAll("mylist","a","b","c");
listOperations.leftPush("mylist","d");
List mylist = listOperations.range("mylist", 0, -1);
System.out.println(mylist);
listOperations.rightPop("mylist");
Long size = listOperations.size("mylist");
System.out.println(size);
}
操作set
/**
* 操作集合类型的数据
*/
@Test
public void testSet(){
//sadd smembers scard sinter sunion srem
SetOperations setOperations = redisTemplate.opsForSet();
setOperations.add("set1","a","b","c","d");
setOperations.add("set2","a","b","x","y");
Set members = setOperations.members("set1");
System.out.println(members);
Long size = setOperations.size("set1");
System.out.println(size);
Set intersect = setOperations.intersect("set1", "set2");
System.out.println(intersect);
Set union = setOperations.union("set1", "set2");
System.out.println(union);
setOperations.remove("set1","a","b");
}
操作zset
/**
* 操作有序集合类型的数据
*/
@Test
public void testZset(){
//zadd zrange zincrby zrem
ZSetOperations zSetOperations = redisTemplate.opsForZSet();
zSetOperations.add("zset1","a",10);
zSetOperations.add("zset1","b",12);
zSetOperations.add("zset1","c",9);
Set zset1 = zSetOperations.range("zset1", 0, -1);
System.out.println(zset1);
zSetOperations.incrementScore("zset1","c",10);
zSetOperations.remove("zset1","a","b");
}
通用操作
/**
* 通用命令操作
*/
@Test
public void testCommon(){
//keys exists type del
Set keys = redisTemplate.keys("*");
System.out.println(keys);
Boolean name = redisTemplate.hasKey("name");
Boolean set1 = redisTemplate.hasKey("set1");
for (Object key : keys) {
DataType type = redisTemplate.type(key);
System.out.println(type.name());
}
redisTemplate.delete("mylist");
}
五、店铺营业状态设置
1. 需求分析和设计
1 产品原型
进到苍穹外卖后台,显示餐厅的营业状态,营业状态分为营业中和打烊中,若当前餐厅处于营业状态,自动接收任何订单,客户可在小程序进行下单操作;若当前餐厅处于打烊状态,不接受任何订单,客户便无法在小程序进行下单操作。
点击营业状态按钮时,弹出更改营业状态
2 接口设计
根据上述原型图设计接口,共包含3个接口。
接口设计:
-
设置营业状态
-
管理端查询营业状态
-
用户端查询营业状态
注:从技术层面分析,其实管理端和用户端查询营业状态时,可通过一个接口去实现即可。因为营业状态是一致的。但是,本项目约定:
-
管理端发出的请求,统一使用/admin作为前缀。
-
用户端发出的请求,统一使用/user作为前缀。
因为访问路径不一致,故分为两个接口实现。
1). 设置营业状态
2). 管理端营业状态
3). 用户端营业状态
3 营业状态存储方式
虽然,可以通过一张表来存储营业状态数据,但整个表中只有一个字段,所以意义不大。
营业状态数据存储方式:基于Redis的字符串来进行存储
2. 代码开发
1 常量类BusinessStatusConstant
准备一个常量类,用于存储 营业状态缓存的key
package com.sky.constant;
public interface BusinessStatusConstant {
/**营业状态缓存的key*/
String BUSINESS_STATUS_KEY = "business:key";
}
2 管理端-设置营业状态
在sky-server模块中,创建AdminShopController.java
package com.sky.controller.admin;
import com.sky.constant.BusinessStatusConstant;
import com.sky.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
@RestController
@Api(tags = "营业状态相关接口")
@RequestMapping("/admin/shop")
public class AdminShopController {
@Autowired
private StringRedisTemplate redisTemplate;
@PutMapping("/{status}")
@ApiOperation("设置营业状态")
public Result setShopStatus(@PathVariable("status") Integer status){
redisTemplate.opsForValue().set(BusinessStatusConstant.BUSINESS_STATUS_KEY, status.toString());
return Result.success();
}
}
2 管理端-查询营业状态
@GetMapping("/status")
@ApiOperation("查询营业状态")
public Result queryShopStatus(){
String status = redisTemplate.opsForValue().get(BusinessStatusConstant.BUSINESS_STATUS_KEY);
return Result.success(Integer.parseInt(status));
}
3 用户端-查询营业状态
创建com.sky.controller.user包,在该包下创建ShopController.java
根据接口定义创建ShopController的getStatus查询营业状态方法:
package com.sky.controller.user;
import com.sky.constant.BusinessStatusConstant;
import com.sky.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Api(tags = "查询营业状态-用户端")
@RequestMapping("/user/shop")
public class UserShopController {
@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping("/status")
@ApiOperation("查询营业状态")
public Result queryShopStatus(){
String status = redisTemplate.opsForValue().get(BusinessStatusConstant.BUSINESS_STATUS_KEY);
if(status == null || "".equals(status)){
return Result.success(0);
}
return Result.success(Integer.parseInt(status));
}
}
3. 功能测试
1 接口文档测试
启动服务:访问http://localhost:8080/doc.html,打开店铺相关接口
注意:使用admin用户登录重新获取token,防止token失效。
设置营业状态:
管理端查询营业状态:
用户端查询营业状态:
2 接口分组展示
在上述接口文档测试中,管理端和用户端的接口放在一起,不方便区分
接下来,我们要实现管理端和用户端接口进行区分。
在WebMvcConfiguration.java中,分别扫描"com.sky.controller.admin"和"com.sky.controller.user"这两个包。
@Bean
public Docket docket1(){
log.info("准备生成接口文档...");
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("管理端接口")
.apiInfo(apiInfo)
.select()
//指定生成接口需要扫描的包
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.admin"))
.paths(PathSelectors.any())
.build();
return docket;
}
@Bean
public Docket docket2(){
log.info("准备生成接口文档...");
ApiInfo apiInfo = new ApiInfoBuilder()
.title("苍穹外卖项目接口文档")
.version("2.0")
.description("苍穹外卖项目接口文档")
.build();
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.groupName("用户端接口")
.apiInfo(apiInfo)
.select()
//指定生成接口需要扫描的包
.apis(RequestHandlerSelectors.basePackage("com.sky.controller.user"))
.paths(PathSelectors.any())
.build();
return docket;
}
3 前后端联调测试
启动nginx,访问 http://localhost