当前位置: 首页 > news >正文

瑞吉外卖之 redis优化缓存

前言

👏作者简介:我是笑霸final,一名热爱技术的在校学生。


📝个人主页:个人主页1 || 笑霸final的主页2


📕系列专栏::本文写在java专栏


📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀


🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏


🐉获取代码 访问gitee:gitee链接

在这里插入图片描述

文章目录

  • 一、环境搭建
  • 二、缓存验证码
  • 三、缓存菜品数据
  • 四、用springCache 优化 套餐数据
    • springCache的使用步骤
    • 缓存套餐数据

一、环境搭建

  • 加入maven坐标
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

  • 配置文件
spring:
  redis:
    host: **************** #ip地址
    port: 6379 #端口号
    password: ***********  #密码
    database: 0
    jedis: #连接池
      pool:
        max-active: 20  #最大连接数,负值表示没有限制,默认8
        max-wait: -1    #最大阻塞等待时间,负值表示没限制,默认-1
        max-idle: 4     #最大空闲连接,默认8
        min-idle: 0     #最小空闲连接,默认0
  • 配置类(不是必须的)
    用来设置序列化的
package com.xbfinal.reggie.config;

import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @autor 笑霸fianl~
 * 欢迎访问GitHub:https://github.com/XBfinal
 * 欢迎访问Gitee:https://gitee.com/XBfianl
 * 欢迎访问CSDN:https://blog.csdn.net/weixin_52062043
 */
public class RedisConfig {
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();

        //设置键的序列化方式
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        //设置值的序列化方式
        redisTemplate.setValueSerializer(new StringRedisSerializer());

        redisTemplate.setConnectionFactory(connectionFactory);
        return redisTemplate;
    }

}

二、缓存验证码

思路:

  • 在服务端UserController 中注入 RedisTemplate对象,用来操作redis
	@Autowired
    private RedisTemplate redisTemplate;
  • 在服务端UserControllersendMsg 方法中 讲随机生成的验证码缓存到redis中,并设置有效期5分钟。
 //将生成的验证码缓存到redis中,并设置有效期5分钟
                redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
  • 在服务端UserControllerlogin 方法中,从redis获取验证码。登陆成功就删除redis中的验证码
		//从redis中获取缓存的验证码
        Object codeInsession = redisTemplate.opsForValue().get(phone);
//如果登陆成功就删除验证码
            redisTemplate.delete(phone);
  • 代码
@RestController
@RequestMapping("/user")
@Slf4j
public class Usercontroller {

    @Autowired
    private UserSerice userSerice;

    @Autowired
    private JavaMailSender mailSender;

    @Autowired
    private RedisTemplate redisTemplate;

    @Value("${spring.mail.username}")
    private String MyFrom;


    /**
     * 发送验证码
     * @param user
     * @return
     */
    @PostMapping("/sendMsg")
    public R<String> sendMsg(@RequestBody User user,
                             HttpSession session){
        log.info("R<String> sendMsg()进来了");
        //获取手机号
        final String phone = user.getPhone();//就不修改实体类了把邮件写进 Phone

        //判断手机号不为空
        if(StringUtils.isNotEmpty(phone)) {
            //生成4位验证码
            final String code =
                    ValidateCodeUtils.generateValidateCode(4).toString();
            System.out.println("==========");
            System.out.println(code);//验证码
            System.out.println("==========");
            //***************************************************/
            //创建简单邮件消息
            SimpleMailMessage message = new SimpleMailMessage();
            message.setFrom(MyFrom);//用自己的邮件发
            //谁要接收
            message.setTo(phone);
            //邮件标题
            message.setSubject("验证码");
            //邮件内容
            message.setText("【笑霸final】你的验证码为:"+code);//
            try {
                mailSender.send(message);
                //需要保存一下验证码,后面用来验证
                //session.setAttribute(phone, code);

                //将生成的验证码缓存到redis中,并设置有效期5分钟
                redisTemplate.opsForValue().set(phone,code,1, TimeUnit.MINUTES);

                return R.success("发送成功");
            } catch (MailException e) {
                e.printStackTrace();
                return R.error("短信发送失败");
            }
        }
        return R.error("短信发送失败");
    }

    /**
     * 登陆
     * @param
     * @param session
     * @return
     */
    @PostMapping("login")
    public R<User> login(@RequestBody Map map,
                           HttpSession session){

        //获取邮箱和验证码
        final String phone = map.get("phone").toString();
        final String code = map.get("code").toString();
        //获取session的验证码并比对
        //final Object codeInsession = session.getAttribute(phone);

        //从redis中获取缓存的验证码
        Object codeInsession = redisTemplate.opsForValue().get(phone);

        if(codeInsession!=null && codeInsession.equals(code)){
            //登陆成功
            //查数据库,没有就存入数据库
            LambdaQueryWrapper<User> queryWrapper
                    =new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone,phone);
             User user = userSerice.getOne(queryWrapper);
            if(user == null){
                //新用户 入库
                 user = new User();
                 user.setPhone(phone);
                 user.setStatus(1);
                userSerice.save(user);
            }else if(user.getStatus()==0){
                //账号被禁用
                return R.error("账号被禁用");
            }
            //成功存入session
            session.setAttribute("user",user.getId());

            //如果登陆成功就删除验证码
            redisTemplate.delete(phone);

            return R.success(user);
        }
        return R.error("验证码和手机不匹配");
    }
}


三、缓存菜品数据

思路

  • 当然 还是要中注入 RedisTemplate对象
  • 改造dishController的list方法,先从redis中获取菜品数据,如果有则直接返回,无需查询数据库;如果没有数据,则查询数据库,并将结果放入redis中
  • 改造dishController的save和update方法,加入清理缓存逻辑(保证数据一致
 @GetMapping("/list")
    public R<List<DishDto>> listR(Dish dish){
        List<DishDto> dtoList=null;
        //先构造key
        String key="dish"+dish.getCategoryId()+"_"+dish.getStatus();

        //先从redis获取缓存数据,如果存在直接返回
        dtoList= (List<DishDto>)redisTemplate.opsForValue().get(key);
        if(dtoList!=null){
                //说明存在
            log.info("在redis中查询的数据");
            return R.success(dtoList);

        }


        //查询条件对象
        LambdaQueryWrapper<Dish> lambdaQueryWrapper
                = new LambdaQueryWrapper();
        lambdaQueryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
        //添加起售状态的条件
        lambdaQueryWrapper.eq(Dish::getStatus,1);
        //添加排序
        lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByAsc(Dish::getUpdateTime);

        final List<Dish> list = dishService.list(lambdaQueryWrapper);
        dtoList  = list.stream().map((item) -> {
            DishDto dishDto = new DishDto();

            BeanUtils.copyProperties(item,dishDto);

            Long categoryId = item.getCategoryId();//分类id
            //根据id查询分类对象
            Category category = categoryService.getById(categoryId);

            if(category != null){
                String categoryName = category.getName();
                dishDto.setCategoryName(categoryName);
            }
            final Long id = item.getId();//当前菜品id
            LambdaQueryWrapper<DishFlavor>lambdaQueryWrapper1
                    =new LambdaQueryWrapper<>();
            lambdaQueryWrapper1.eq(DishFlavor::getDishId,id);
            final List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper1);
            dishDto.setFlavors(dishFlavorList);

            return dishDto;
        }).collect(Collectors.toList());

        //redis不存在,则查询数据库并加入缓存,设置1小时的缓存时间
        redisTemplate.opsForValue().set(key,dtoList,1, TimeUnit.HOURS);

        return R.success(dtoList);
    }

注意 :使用缓存的过程中要保证数据库和redis中的数据一致。当数据库发生变化时,需要及时清理缓存数据,在save和update方法添加如下代码

  • 清理所有菜品的缓存
//清理所有菜品的缓存
       final Set keys = redisTemplate.keys("dish_*");
       redisTemplate.delete(keys);
  • 精确清理
 //精确清理
        String key="dish"+dishDto.getCategoryId()+"_"+dishDto.getStatus();
        redisTemplate.delete(key);
        

四、用springCache 优化 套餐数据

springCache的使用步骤

  • 第一步:导入springCache的maven坐标
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-cache -->
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-cache</artifactId>
   <version>2.7.0</version>
</dependency>

  • 第二步:配置yml文件
spring:
  cache:
    redis:
      time-to-live: 1800000 #设置缓存的有效期
    type: redis #缓存类型redis
  • 第三步:在启动类上加入@EnableCaching注解,开启缓存功能
  • 第四步:在controller的方法上加入相对应的注解进行缓存操作

常用注解
@CacheEvict :应用到移除数据的方法上,如删除方法,调用方法时会从缓存中移除相应的数据
@Cacheable:应用到读取数据的方法上,即可缓存的方法,如查找方法,先从缓存中读取,如果没有再调用相应方法获取数据,然后把数据添加到缓存中
@CachePut :应用到写数据的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存

Cacheable的参数如下(其他的注解也大同小异)

  • value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
  • key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
  • condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存。
  • unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
  • keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的

缓存套餐数据

思路:
注意返回类型为R的要实现序列化接口Serializable

public class R<T> implements Serializable
  • SetmealController的list方法上加入@Cacheable
  • SetmealController的save和delete、saveUpdate、stop方法上加入@CacheEvict

加入以下代码即可

  @CacheEvict(value = "setmealCache" ,allEntries = true)//allEntries = true清理setmealCache下所有的缓存
  
  @Cacheable(value = "setmealCache",key = "#setmeal.categoryId")

在这里插入图片描述

是否忘记了springboot如何操作redis??
点此跳转 《springDataRedis操作》的复习吧

相关文章:

  • [JavaWeb]—前端篇
  • 机器学习感知机原理及python代码实现
  • js 对象
  • 图解Redis 记录
  • 网络安全的行业黑话 ——防守篇之软硬件
  • 使用 CubeMX 配置 RCC 时钟
  • CVPR 2022 Oral 大连理工提出的SCI 快速、超强的低光照图像增强方法 可视化代码
  • CVE-2013-4547 Nginx文件名解析漏洞详解
  • 信息收集之 操作系统识别
  • 程序设计——图书管理系统(附源代码)
  • 纯C实现的贪吃蛇(无EaxyX,详解)
  • 布局管理器案例集锦
  • STL 源码阅读笔记-类型萃取(Traits)
  • 【MySQL 第三天数据库表 增删改查】
  • 【白板推导系列笔记】降维-样本均值样本方差矩阵
  • 【Amaple教程】5. 插件
  • 【comparator, comparable】小总结
  • 【mysql】环境安装、服务启动、密码设置
  • 30秒的PHP代码片段(1)数组 - Array
  • ES学习笔记(12)--Symbol
  • httpie使用详解
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • Java到底能干嘛?
  • spring boot下thymeleaf全局静态变量配置
  • springMvc学习笔记(2)
  • 笨办法学C 练习34:动态数组
  • 闭包--闭包之tab栏切换(四)
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 官方新出的 Kotlin 扩展库 KTX,到底帮你干了什么?
  • 跨域
  • 实现菜单下拉伸展折叠效果demo
  • 通过npm或yarn自动生成vue组件
  • 一起参Ember.js讨论、问答社区。
  • RDS-Mysql 物理备份恢复到本地数据库上
  • ​你们这样子,耽误我的工作进度怎么办?
  • # 手柄编程_北通阿修罗3动手评:一款兼具功能、操控性的电竞手柄
  • #我与Java虚拟机的故事#连载08:书读百遍其义自见
  • (LNMP) How To Install Linux, nginx, MySQL, PHP
  • (顶刊)一个基于分类代理模型的超多目标优化算法
  • (二)JAVA使用POI操作excel
  • (篇九)MySQL常用内置函数
  • (七)理解angular中的module和injector,即依赖注入
  • (算法)Game
  • (算法)前K大的和
  • (一)RocketMQ初步认识
  • (一)Thymeleaf用法——Thymeleaf简介
  • (转)甲方乙方——赵民谈找工作
  • *Django中的Ajax 纯js的书写样式1
  • .bat批处理(六):替换字符串中匹配的子串
  • .htaccess配置常用技巧
  • .locked1、locked勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复
  • .NET CORE使用Redis分布式锁续命(续期)问题
  • .NET 材料检测系统崩溃分析
  • .NET/C# 使用 ConditionalWeakTable 附加字段(CLR 版本的附加属性,也可用用来当作弱引用字典 WeakDictionary)
  • .pyc文件是什么?