资讯动态

苍穹外卖-day05(SpringBoot+SSM的企业级Java项目实战)

发布时间:2026/8/21 18:37:07 来源:尧图企业网站定制
苍穹外卖-day05课程内容Redis入门Redis数据类型Redis常用命令在Java中操作Redis店铺营业状态设置功能实现营业状态设置效果图选择营业中客户可在小程序端下单选择打烊中客户无法在小程序端下单1. Redis入门1.1 Redis简介Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件。官网https://redis.io中文网Redis中文网key-value结构存储主要特点基于内存存储读写性能高适合存储热点数据热点商品、资讯、新闻企业应用广泛Redis是用C语言开发的一个开源的高性能键值对(key-value)数据库官方提供的数据是可以达到100000的QPS每秒内查询次数。它存储的value类型比较丰富也被称为结构化的NoSql数据库。NoSqlNot Only SQL不仅仅是SQL泛指非关系型数据库。NoSql数据库并不是要取代关系型数据库而是关系型数据库的补充。关系型数据库(RDBMS)MysqlOracleDB2SQLServer非关系型数据库(NoSql)RedisMongo dbMemCached1.2 Redis下载与安装1.2.1 Redis下载Redis安装包分为windows版和Linux版Windows版下载地址Releases · microsoftarchive/redis · GitHubLinux版下载地址 Index of /releases/资料中已提供好的安装包1.2.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-serverRedis服务启动脚本/usr/local/redis-4.0.0/src/redis-cliRedis客户端脚本/usr/local/redis-4.0.0/redis.confRedis配置文件1.3 Redis服务启动与停止以window版Redis进行演示1.3.1 服务启动命令redis-server.exe redis.windows.confRedis服务默认端口号为6379通过快捷键Ctrl C即可停止Redis服务当Redis服务启动成功后可通过客户端进行连接。1.3.2 客户端连接命令redis-cli.exe通过redis-cli.exe命令默认连接的是本地的redis服务并且使用默认6379端口。也可以通过指定如下参数连接-h ip地址-p 端口号-a 密码如果需要1.3.3 修改Redis配置文件设置Redis服务密码修改redis.windows.confrequirepass 123456注意修改密码后需要重启Redis服务才能生效Redis配置文件中 # 表示注释重启Redis后再次连接Redis时需加上密码否则连接失败。redis-cli.exe -h localhost -p 6379 -a 123456此时-h 和 -p 参数可省略不写。1.3.4 Redis设置开机自启在 CMD 中切换到 Redis 解压目录示例命令需替换为你的实际路径cd /d D:\develop\Redis-x64-3.2.100执行注册服务命令指定配置文件确保服务启动时加载正确配置redis-server --service-install redis.windows.conf --loglevel verbose --service-name Redis命令说明--service-install注册为系统服务。redis.windows.conf指定 Redis 配置文件必须带否则可能启动失败。--loglevel verbose日志级别可选方便排查问题。--service-name Redis服务名称可自定义如 “Redis6”后续在服务面板中显示此名称。设置开机自启并启动服务按下Win R输入services.msc打开「服务」面板。找到刚才注册的服务名称为Redis或你自定义的名称。右键 → 「属性」→ 「启动类型」选择「自动」→ 「应用」→ 「确定」。右键 → 「启动」启动 Redis 服务启动成功后服务状态会显示「正在运行」。1.3.5 Redis客户端图形工具默认提供的客户端连接工具界面不太友好同时操作也较为麻烦接下来引入一个Redis客户端图形工具。在当天资料中已提供安装包直接安装即可。安装完毕后直接双击启动新建连接连接成功前提命令窗口启动redis服务2. Redis数据类型2.1 五种常用数据类型介绍Redis存储的是key-value结构的数据其中key是字符串类型value有5种常用的数据类型字符串 string哈希 hash列表 list集合 set有序集合 sorted set / zset2.2 各种数据类型特点解释说明字符串(string)普通字符串Redis中最简单的数据类型哈希(hash)也叫散列类似于Java中的HashMap结构列表(list)按照插入顺序排序可以有重复元素类似于Java中的LinkedList集合(set)无序集合没有重复元素类似于Java中的HashSet有序集合(sorted set/zset)集合中每个元素关联一个分数(score)根据分数升序排序没有重复元素3. Redis常用命令3.1 字符串操作命令Redis 中字符串类型常用命令SETkey value 设置指定key的值GETkey 获取指定key的值SETEXkey seconds value 设置指定key的值并将 key 的过期时间设为 seconds 秒SETNXkey value 只有在 key 不存在时设置 key 的值更多命令可以参考Redis中文网https://www.redis.net.cn3.2 哈希操作命令Redis hash 是一个string类型的 field 和 value 的映射表hash特别适合用于存储对象常用命令HSETkey field value 将哈希表 key 中的字段 field 的值设为 valueHGETkey field 获取存储在哈希表中指定字段的值HDELkey field 删除存储在哈希表中的指定字段HKEYSkey 获取哈希表中所有字段HVALSkey 获取哈希表中所有值3.3 列表操作命令Redis 列表是简单的字符串列表按照插入顺序排序常用命令LPUSHkey value1 [value2] 将一个或多个值插入到列表头部LRANGEkey start stop 获取列表指定范围内的元素RPOPkey 移除并获取列表最后一个元素LLENkey 获取列表长度BRPOPkey1 [key2 ] timeout 移出并获取列表的最后一个元素 如果列表没有元素会阻塞列表直到等待超 时或发现可弹出元素为止3.4 集合操作命令Redis set 是string类型的无序集合。集合成员是唯一的这就意味着集合中不能出现重复的数据常用命令SADDkey member1 [member2] 向集合添加一个或多个成员SMEMBERSkey 返回集合中的所有成员SCARDkey 获取集合的成员数SINTERkey1 [key2] 返回给定所有集合的交集SUNIONkey1 [key2] 返回所有给定集合的并集SREMkey member1 [member2] 移除集合中一个或多个成员3.5 有序集合操作命令Redis有序集合是string类型元素的集合且不允许有重复成员。每个元素都会关联一个double类型的分数。常用命令常用命令ZADDkey score1 member1 [score2 member2] 向有序集合添加一个或多个成员ZRANGEkey start stop [WITHSCORES] 通过索引区间返回有序集合中指定区间内的成员ZINCRBYkey increment member 有序集合中对指定成员的分数加上增量 incrementZREMkey member [member ...] 移除有序集合中的一个或多个成员3.6 通用命令Redis的通用命令是不分数据类型的都可以使用的命令KEYS pattern 查找所有符合给定模式( pattern)的 keyEXISTS key 检查给定 key 是否存在TYPE key 返回 key 所储存的值的类型DEL key 该命令用于在 key 存在是删除 key4.在Java中操作Redis4.1 Redis的Java客户端前面我们讲解了Redis的常用命令这些命令是我们操作Redis的基础那么我们在java程序中应该如何操作Redis呢这就需要使用Redis的Java客户端就如同我们使用JDBC操作MySQL数据库一样。Redis 的 Java 客户端很多常用的几种JedisLettuceSpring Data RedisSpring 对 Redis 客户端进行了整合提供了 Spring Data Redis在Spring Boot项目中还提供了对应的Starter即 spring-boot-starter-data-redis。我们重点学习Spring Data Redis。4.2 Spring Data Redis使用方式4.2.1 介绍Spring Data Redis 是 Spring 的一部分提供了在 Spring 应用中通过简单的配置就可以访问 Redis 服务对 Redis 底层开发包进行了高度封装。在 Spring 项目中可以使用Spring Data Redis来简化 Redis 操作。网址Spring Data RedisSpring Boot提供了对应的Startermaven坐标dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependencySpring Data Redis中提供了一个高度封装的类RedisTemplate对相关api进行了归类封装,将同一类型操作封装为operation接口具体分类如下ValueOperationsstring数据操作SetOperationsset类型数据操作ZSetOperationszset类型数据操作HashOperationshash类型的数据操作ListOperationslist类型的数据操作4.2.2 环境搭建进入到sky-server模块1). 导入Spring Data Redis的maven坐标(已完成)dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency2). 配置Redis数据源在application-dev.yml中添加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). 编写配置类创建RedisTemplate对象package com.sky.config; ​ import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; ​ Configuration Slf4j public class RedisConfiguration { ​ Bean public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ log.info(开始创建redis模板对象...); RedisTemplate redisTemplate new RedisTemplate(); //设置redis的连接工厂对象 redisTemplate.setConnectionFactory(redisConnectionFactory); //设置redis key的序列化器 redisTemplate.setKeySerializer(new StringRedisSerializer()); return redisTemplate; } }解释说明当前配置类不是必须的因为 Spring Boot 框架会自动装配 RedisTemplate 对象但是默认的key序列化器为JdkSerializationRedisSerializer导致我们存到Redis中后的数据和原始数据有差别故设置为StringRedisSerializer序列化器。4). 通过RedisTemplate对象操作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 RedisTemplate 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(); } }测试提前使用redis-server.exe redis.windows.conf启动redis服务说明RedisTemplate对象注入成功并且通过该RedisTemplate对象获取操作5种数据类型相关对象。上述环境搭建完毕后接下来我们就来具体对常见5种数据类型进行操作。4.2.3 操作常见类型数据1). 操作字符串类型数据/** * 操作字符串类型的数据 */ Test public void testString() { // set get setex setnx // set设置key-value无过期时间 redisTemplate.opsForValue().set(name, 小明); // get根据key获取value返回Object需要强转String String city (String) redisTemplate.opsForValue().get(name); // 控制台打印获取到的值小明 System.out.println(city); // setex设置key-value同时指定过期时间3分钟后自动失效 redisTemplate.opsForValue().set(code, 1234, 3, TimeUnit.MINUTES); // setIfAbsent 等价 SETNXkey不存在才设置keylock原本不存在设置值为1返回true redisTemplate.opsForValue().setIfAbsent(lock, 1); // setIfAbsentkey lock已经存在不会覆盖修改设置失败返回falselock值依旧是1 redisTemplate.opsForValue().setIfAbsent(lock, 2); }2). 操作哈希类型数据/** * 操作哈希类型的数据 */ Test public void testHash(){ //hset hget hdel hkeys hvals // 获取Hash类型操作对象对应redis hash相关命令 HashOperations hashOperations redisTemplate.opsForHash(); // hset key field value向hash key100中存入field:namevalue:tom hashOperations.put(100,name,tom); // hset key field value向hash key100中存入field:agevalue:20 hashOperations.put(100,age,20); // hget key field获取hash中key100字段name对应的值 String name (String) hashOperations.get(100, name); System.out.println(name); // hkeys key获取hash key100下所有的field字段名返回Set集合 Set keys hashOperations.keys(100); System.out.println(keys); // hvals key获取hash key100下所有的value字段值返回List集合 List values hashOperations.values(100); System.out.println(values); // hdel key field删除hash key100中的age字段 hashOperations.delete(100,age); }3). 操作列表类型数据/** * 操作列表类型的数据 */ Test public void testList() { //lpush lrange rpop llen // 获取List类型操作对象对应redis list相关命令 ListOperations listOperations redisTemplate.opsForList(); // lpush mylist a b c从列表【左侧】一次性批量推入多个元素 a、b、c listOperations.leftPushAll(mylist, a, b, c); // lpush mylist d从列表【左侧】推入单个元素 d listOperations.leftPush(mylist, d); // lrange mylist 0 -1截取列表0是起始下标-1代表取全部元素返回List集合 List mylist listOperations.range(mylist, 0, -1); System.out.println(mylist); // rpop mylist从列表【右侧】弹出一个元素弹出后该元素会从集合中移除 listOperations.rightPop(mylist); // llen mylist获取list集合的元素总个数 Long size listOperations.size(mylist); System.out.println(size); }4). 操作集合类型数据/** * 操作集合类型的数据 */ Test public void testSet() { //sadd smembers scard sinter sunion srem // 获取Set类型操作对象对应redis set集合相关命令集合元素不允许重复 SetOperations setOperations redisTemplate.opsForSet(); // sadd set1 a b c d向set1集合批量添加元素a,b,c,d自动去重 setOperations.add(set1, a, b, c, d); // sadd set2 a b x y向set2集合批量添加元素a,b,x,y setOperations.add(set2, a, b, x, y); // smembers set1获取set1集合中全部的元素返回Set集合 Set members setOperations.members(set1); System.out.println(members); // scard set1获取set1集合中元素的数量 Long size setOperations.size(set1); System.out.println(size); // sinter set1 set2求两个集合的【交集】同时存在于set1和set2的元素 Set intersect setOperations.intersect(set1, set2); System.out.println(intersect); // sunion set1 set2求两个集合的【并集】合并两个集合所有元素自动去重 Set union setOperations.union(set1, set2); System.out.println(union); // srem set1 a b删除set1集合中的a、b两个元素 setOperations.remove(set1, a, b); }5). 操作有序集合类型数据/** * 操作有序集合类型的数据 */ Test public void testZset() { //zadd zrange zincrby zrem // 获取ZSet有序集合操作对象元素带score分数会根据分数排序元素不能重复 ZSetOperations zSetOperations redisTemplate.opsForZSet(); // zadd zset1 10 a添加元素a分数10zadd key member score zSetOperations.add(zset1, a, 10); // zadd zset1 12 b添加元素b分数12 zSetOperations.add(zset1, b, 12); // zadd zset1 9 c添加元素c分数9 zSetOperations.add(zset1, c, 9); // zrange zset1 0 -1按分数从小到大获取全部元素(0起始下标-1代表取所有) Set zset1 zSetOperations.range(zset1, 0, -1); System.out.println(zset1); // zincrby zset1 10 c给元素c的score分数 10c原来9加完变成19 zSetOperations.incrementScore(zset1, c, 10); // zrem zset1 a b删除有序集合中的a、b两个元素 zSetOperations.remove(zset1, a, b); }6). 通用命令操作/** * 通用命令操作 */ Test public void testCommon() { //keys exists type del // keys *查询redis中所有的key通配符*代表匹配全部 Set keys redisTemplate.keys(*); System.out.println(keys); // exists key判断key是否存在存在返回true不存在返回false Boolean name redisTemplate.hasKey(name); Boolean set1 redisTemplate.hasKey(set1); // 遍历所有key获取每一个key对应的数据类型 for (Object key : keys) { // type key查看该key存储的数据类型 DataType type redisTemplate.type(key); System.out.println(type.name()); } // del key删除指定的key以及对应的数据 redisTemplate.delete(mylist); }5. 店铺营业状态设置5.1 需求分析和设计5.1.1 产品原型进到苍穹外卖后台显示餐厅的营业状态营业状态分为营业中和打烊中若当前餐厅处于营业状态自动接收任何订单客户可在小程序进行下单操作若当前餐厅处于打烊状态不接受任何订单客户便无法在小程序进行下单操作。点击营业状态按钮时弹出更改营业状态选择营业设置餐厅为营业中状态选择打烊设置餐厅为打烊中状态状态说明5.1.2 接口设计根据上述原型图设计接口共包含3个接口。接口设计设置营业状态管理端查询营业状态用户端查询营业状态注从技术层面分析其实管理端和用户端查询营业状态时可通过一个接口去实现即可。因为营业状态是一致的。但是本项目约定管理端发出的请求统一使用/admin作为前缀。用户端发出的请求统一使用/user作为前缀。因为访问路径不一致故分为两个接口实现。1). 设置营业状态2). 管理端营业状态3). 用户端营业状态5.1.3 营业状态存储方式虽然可以通过一张表来存储营业状态数据但整个表中只有一个字段所以意义不大。营业状态数据存储方式基于Redis的字符串来进行存储约定1表示营业 0表示打烊5.2 代码开发5.2.1 设置营业状态在sky-server模块中创建ShopController.java根据接口定义创建ShopController的setStatus设置营业状态方法package com.sky.controller.admin; import com.sky.result.Result; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; RestController(adminShopController) RequestMapping(/admin/shop) Api(tags 店铺相关接口) Slf4j public class ShopController { public static final String KEY SHOP_STATUS; Autowired private RedisTemplate redisTemplate; /** * 设置店铺的营业状态 * param status * return */ PutMapping(/{status}) ApiOperation(设置店铺的营业状态) public Result setStatus(PathVariable Integer status){ log.info(设置店铺的营业状态为{},status 1 ? 营业中 : 打烊中); redisTemplate.opsForValue().set(KEY,status); return Result.success(); } }5.2.2 管理端查询营业状态根据接口定义创建ShopController的getStatus查询营业状态方法/** * 获取店铺的营业状态 * return */ GetMapping(/status) ApiOperation(获取店铺的营业状态) public ResultInteger getStatus(){ Integer status (Integer) redisTemplate.opsForValue().get(KEY); log.info(获取到店铺的营业状态为{},status 1 ? 营业中 : 打烊中); return Result.success(status); }5.2.3 用户端查询营业状态创建com.sky.controller.user包在该包下创建ShopController.java根据接口定义创建ShopController的getStatus查询营业状态方法package com.sky.controller.user; import com.sky.result.Result; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; RestController(userShopController) RequestMapping(/user/shop) Api(tags 店铺相关接口) Slf4j public class ShopController { public static final String KEY SHOP_STATUS; Autowired private RedisTemplate redisTemplate; /** * 获取店铺的营业状态 * return */ GetMapping(/status) ApiOperation(获取店铺的营业状态) public ResultInteger getStatus(){ Integer status (Integer) redisTemplate.opsForValue().get(KEY); log.info(获取到店铺的营业状态为{},status 1 ? 营业中 : 打烊中); return Result.success(status); } }5.3 功能测试5.3.1 接口文档测试启动服务访问http://localhost:8080/doc.html打开店铺相关接口注意使用admin用户登录重新获取token防止token失效。设置营业状态点击发送查看Idea控制台日志查看Redis中数据管理端查询营业状态用户端查询营业状态5.3.2 接口分组展示在上述接口文档测试中管理端和用户端的接口放在一起不方便区分。接下来我们要实现管理端和用户端接口进行区分。先删除默认的如下在WebMvcConfiguration.java中分别扫描com.sky.controller.admin和com.sky.controller.user这两个包。/** * 通过knife4j生成接口文档 * * return */ 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; }重启服务器再次访问接口文档可进行选择用户端接口或者管理端接口5.3.3 前后端联调测试启动nginx,访问 http://localhost进入后台状态为营业中点击营业状态设置修改状态为打烊中再次查看状态状态已为打烊中5.4 代码提交点击提交提交过程中出现提示继续push:推送成功

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价