一、缓存机制的原理

  • 一个系统在面向用户使用的时候,当用户的数量不断增多,那么请求次数也会不断增多,当请求次数增多的时候,就会造成请求压力,而我们当前的所有数据查询都是从数据库MySQL中直接查询的,那么就可能会产生如下问题
    • 频繁访问数据库,数据库访问压力大,系统性能下降,用户体验差
  • 解决问题的方法
    • 要解决上述提到的问题,就可以使用前面学习的Redis技术,通过Redis实现缓存机制,从而降低数据库的访问压力;提高系统的访问性能,从而提升用户体验
    • 加入Redis后,在进行数据查询的时候,就需要先查询缓存,如果缓存中有数据,直接返回;如果没有相对应的数据,那么就去查询数据库,再将数据库查询的结果,缓存在Redis中

二、缓存短信验证码

环境搭建

  • ①、在项目pom.xml文件中导入spring-data-redis的maven坐标

    •        <!--Spring data redis-->
             <dependency>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-starter-data-redis</artifactId>
             </dependency>
      
  • ②、在项目的配置文件中加入Redis相关配置(在Spring层级下)

    •   redis:
          jedis:
            pool:
              max-idle: 5 #最大链接数,连接池中最多有10个
              min-idle: 1 # 最大空闲数
              max-wait: 1000ms #连接池最大阻塞等待时间
              max-active: 10 #最大链接数
          host: 127.0.0.1
          port: 6379
          database: 2
          # password: 
      

2.1、思路分析

  • 前面实现的移动端手机验证登录功能,随机生成的验证码是保存在HttpSession当中的。但是实际的业务场景中,一般验证码都是需要设置过期时间的,如果存在HttpSession中就无法设置过期时间,此时我们就需要对这一块的功能进行优化
  • 可以将验证码缓存在Redis中,具体的实现思路如下
    • ①、在服务端UserController中注入RedisTemplate对象,用于操作Redis
    • ②、在服务端UserControllersendMsg方法中,将随机生成的验证码缓存到Redis中,并设置有效期为5分组
    • ③、在服务端UserControllerlogin方法中,从Redis中获取缓存的验证码,如果登录成功则删除Redis中的验证码

2.2、代码改造

  • ①、在UserController中注入RedisTemplate对象,用于操作Redis

    •     @Autowired
          private RedisTemplate<String, String> redisTemplate;
      
  • ②、在UserControllersendMsg方法中,将生成的验证码保存到Redis中(为了测试方便,这里是直接生成了固定的验证码,没有调用真实的生成验证码的API)

    • // 将登录账号的信息存储在redis中
              // 获取字符串的客户端
              ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
              // 存储验证码,让验证码失效时间是一分钟
              valueOperations.set("SMS_" + user.getPhone(), code, 1, TimeUnit.MINUTES);
      
  • ③、在登录校验的代码中实现从Redis中取出数据

    •        // 2. 获取正确的验证码
           //        String verifyCode = (String) session.getAttribute("SMS_" + inputPhone);
      
             // 从redis中获取正确的验证码
             String verifyCode = redisTemplate.opsForValue().get("SMS_" + inputPhone);
      

2.3、功能测试

  • ①、访问前端,获取验证码
    • 通过控制台的日志,可以看到生成的代码
  • ②、通过Redis的图形化界面工具查看Redis中的数据
  • ③、在登录界面填写验证码登录完成后,查看Redis中的数据是否删除

三、缓存菜品信息

3.1、思路分析

  • 之前项目中已经实现了移动端菜品查看的功能,对应的服务端方法为DishControllerlist方法,此方法会根据前端提交的查询条件(categoryId)进行数据库查询操作。

  • 在高并发的情况下,频繁查询数据库会导致系统性能下降,服务端响应时间增长;针对这个问题,可以对此方法进行缓存优化,提高系统的性能

  • 那么应该缓存多少分数据呢?是所有的菜品缓存一份,还是需要根据分类的不同,缓存多份?

    • 很显然,在前端点击一个分类的时候,展示的就是这个分类下的菜品,其他菜品无需展示
    • 所以,这里面我们在缓存时,可以根据菜品的分类,缓存多分数据,页面在提交查询请求的时候,就查询该分类下的菜品缓存数据
  • 具体实现思路

    • ①、修改业务层的list方法,先从Redis中获取分类对应的菜品数据,如果有则直接返回,无需查询数据库;如果没有,则查询数据库,并将查询到的菜品数据存入Redis
    • ②、修改DishControllersaveupdate方法,加入清理缓存的逻辑
  • 注意事项

    • 在使用缓存的过程当中,要注意保证数据库中的数据和缓存中的数据保持一致
    • 如果数据库中的数据发生变化,需要及时清理缓存数据。否则就会造成缓存数据与数据库数据不一致的情况

3.2、代码改造

3.2.1、查询菜品缓存

在增加缓存之前,需要对存储进Redis中的数据进行一个简单的设计,如下所示

数据类型 key值 value值
String dish_菜品分类的id 菜品的List集合(List )
  • ①、在DishServiceImpl中注入RedisTemplate

    •     @Autowired
          private RedisTemplate<String, String> redisTemplate;
      
  • ②、在list方法中,查询数据库之前,先查询缓存,如果缓存有数据,则直接返回

    •     // 根据分类id查询菜品列表数据
          @Override
          public List<DishDto> selectByCategoryIdAndStatus(Long categoryId, Integer status) {
              // 0. 首先先判断Redis中是否存在缓存
              // 获取redis操作字符串的客户端
              ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
              List<DishDto> dishDtoList = JSON.parseObject(valueOperations.get("dish_" + categoryId + "_" + status), List.class);
              // 如果redis中不存在这个缓存,则查询数据库,并且将查询到的结果存储到缓存中
              if (dishDtoList == null) {
                  // 1. 调用 dao 层对象执行sql语句查询数据
                  List<Dish> dishList = dishMapper.selectByCategoryIdAndStatus(categoryId, status);
                  // 2. 遍历dishList,查询其相对应的口味数据表
                  dishDtoList = dishList.stream().map(dish -> {
                      // 查询对应的口味表
                      List<DishFlavor> dishFlavorList = dishFlavorMapper.selectByDishId(dish.getId());
                      DishDto dishDto = new DishDto();
                      // 将数据封装到dishDto中
                      dishDto.setFlavors(dishFlavorList);
                      // 将基本属性复制给dishDto
                      BeanUtils.copyProperties(dish, dishDto);
      
                      return dishDto;
                  }).collect(Collectors.toList());
      
                  // 把查询到的数据,存储到Redis中
                  // 把dishDtoList对象转换为Json格式
                  String dishJson = JSON.toJSONString(dishDtoList);
                  valueOperations.set("dish_" + categoryId, dishJson, 2, TimeUnit.DAYS);
              }
      
              // 返回数据
              return dishDtoList;
          }
      

3.2.2、清理菜品缓存

为了保证数据库中的数据和缓存中的数据一致,如果数据库中的数据发生变化,需要及时清理缓存数据

所以,需要在菜品的增删改中清空缓存数据

  • 清理菜品缓存的方式有两种

    • A、清理所有分类下的菜品缓存

      • //清理所有菜品的缓存数据
        Set keys = redisTemplhate.keys("dish_*"); //获取所有以dish_xxx开头的key
        redisTemplate.delete(keys); //删除这些key
        
    • B、清理当前添加菜品分类下的缓存

      • //清理某个分类下面的菜品缓存数据
        String key = "dish_" + dishDto.getCategoryId();
        redisTemplate.delete(key);
        
    • 两者的优劣(需要结合实际的业务场景考虑)

      • 对于这次的修改操作,用户可以修改菜品的分类,如果用户修改了了菜品的分类,那么原来的分类下将少一个菜品,新的分类下将多一个菜品,这样的话,两个分类的菜品列表数据都发生了变化
      • 即此时的情况不能只是删除某一个分类的菜品缓存
      • 所以,在本次的系统中推荐使用第一种方法清理菜品缓存
  • 这里清理缓存的操作比较简单,就不演示了,只需要在数据发生变更后的代码后面添加一个删除缓存的代码即可,如下所示

3.3、功能测试

  • ①、访问移动端,根据分类查询菜品列表,然后再检查Redis的缓存数据是否存在
  • ②、当对菜品进行增删改的时候,查询Redis中的缓存数据,是否被清除

四、Spring Cache

4.1、Spring Cache介绍

  • Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能,大大简化我们在业务中操作缓存的代码

  • Spring Cache只是提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术。CacheManager是Spring提供的各种缓存技术抽象接口

  • 针对不同的缓存技术需要实现不同的CacheManager,如下表所示

    • CacheManager 描述
      EhCacheCacheManager 使用EhCache作为缓存技术
      GuavaCacheManager 使用Google的GuavaCache作为缓存技术
      RedisCacheManager 使用Redis作为缓存技术
      spring 自己也搞了一套缓存技术,默认的缓存
      spring缓存是缓存在Map集合中

4.2、Spring Cache注解

  • 在SpringCache中提供了很多缓存操作的注解,常见的几个如下所示

    • 注解 说明
      @EnableCaching 开启缓存注解功能
      @Cacheable 在方法执行前Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,则调用方法并将方法返回值放到缓存中
      @CachePut 将方法的返回值或者参数放到缓存中
      @CacheEvict 将一条或多条数据从缓存中删除
  • 在SpringBoot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可

    • 例如使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标,同时在配置文件中配置Redis的相关配置即可

4.3、Spring Cache入门案例

  • 接下来,我们可以通过一个入门案例演示以下SpringCache的常见用法。上面提到,SpringCache可以集成不同的缓存技术,如Redis、Ehcache甚至我们可以使用Map来缓存数据,接下来我们在演示的时候,就先通过一个Map来缓存数据,最后我们再换成Redis来缓存

4.3.1、环境准备

  • ①、数据库准备

    • /*
      SQLyog Ultimate v11.33 (64 bit)
      MySQL - 5.5.40 : Database - cache_demo
      *********************************************************************
      */
      /*!40101 SET NAMES utf8 */;
      /*!40101 SET SQL_MODE=''*/;
      /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
      /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
      /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
      /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
      CREATE DATABASE /*!32312 IF NOT EXISTS*/`cache_demo` /*!40100 DEFAULT CHARACTER SET utf8 */;
      
      USE `cache_demo`;
      
      /*Table structure for table `user` */
      
      DROP TABLE IF EXISTS `user`;
      
      CREATE TABLE `user` (
                              `id` bigint(20) NOT NULL AUTO_INCREMENT,
                              `name` varchar(255) DEFAULT NULL,
                              `age` int(11) DEFAULT NULL,
                              `address` varchar(255) DEFAULT NULL,
                              PRIMARY KEY (`id`)
      ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
      
      /*Data for the table `user` */
      
      LOCK TABLES `user` WRITE;
      
      UNLOCK TABLES;
      
      /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
      /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
      /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
      /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
      
  • ②、导入基本工程

    • 基本工程的创建,这里就不演示了,只是一个User表的增删改查操作
  • ③、注入CacheManager

    • 我们可以在UserController注入一个CacheManager,在Debug时,我们可以通过CacheManger跟踪缓存中数据的变化
    • 我们可以进入CacheManger接口的源码中查看,默认的实现有几种,如下图所示
    • 而在上述的几个实现中,默认使用的是ConcurrentMapCacheManger,稍后我们可以通过断点的形式跟踪缓存数据的变化
  • ④、启动类加上@EnableCaching注解

    • 在启动类加上该注解,就代表当前项目开启缓存注解功能

4.3.2、@CachePut注解

  • @CachePut注解说明
    • 作用
      • 将方法返回值,放入缓存
    • value
      • 缓存的名称,每个缓存名称下面可以有很多key
    • key
      • 缓存的key,支持Spring的表达式语言SPEL语法
  • ①、在save方法上加上注解@CachePut

    • 当前UserControllersave方法是用来保存用户信息的,我们希望在该用户信息保存到数据库的同时,也往缓存中缓存一份数据,我们可以在save方法上加上注解 @CachePut,如下所示

    •     /**
           * CachePut:将方法返回值放入缓存
           * value:缓存的名称,每个缓存名称下面可以有多个key
           * key:缓存的key
           */
          @CachePut(value = "userCache", key = "#user.id")
          @PostMapping
          public User save(@RequestBody  User user){
              userService.save(user);
              return user;
          }
      
    • key的写法如下:

    • #user.id
      • #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key
    • #user.name
      • #user指的是方法形参的名称, name指的是user的name属性 ,也就是使用user的name属性作为key
    • #result.id
      • #result代表方法返回值,该表达式 代表以返回对象的id属性作为key
    • #result.name
      • #result代表方法返回值,该表达式 代表以返回对象的name属性作为key
  • ②、使用Postman进行功能测试

    • 启动服务,通过postman请求访问UserController的方法, 然后通过断点(Debug)的形式跟踪缓存数据
    • 第一次访问时,缓存中的数据是空的,因为save方法执行完毕后才会缓存数据
    • 第二次访问时,我们通过debug可以看到已经有一条数据了,就是上次保存的数据,已经缓存了,缓存的key就是用户的id
  • PS

    • 上述的演示,最终的数据,实际上是缓存在ConcurrentHashMap中,那么当我们的服务器重启之后,缓存中的数据就会丢失。 后面使用了Redis来缓存就不存在这样的问题

4.3.3、@CacheEvict注解

  • @CacheEvict注解说明
    • 作用
      • 清理指定缓存
    • value
      • 缓存的名称,每个缓存名称下面可以有多个key
    • key
      • 缓存的key,支持Spring的表达式语言SPEL语法
  • ①、在delete方法上加@CacheEvict注解

    • 当我们在删除数据库user表数据的时候,需要删除缓存中对应的数据,此时就可以使用@CacheEvict注解,如下所示

      •     /**
             * CacheEvict:清理指定缓存
             * value:缓存的名称,每个缓存名称下面可以有多个key
             * key:缓存的key
             */
            @CacheEvict(value = "userCache", key = "#p0")
            //#p0 代表第一个参数
            //@CacheEvict(value = "userCache",key = "#root.args[0]") //#root.args[0] 代表第一个参数
            //@CacheEvict(value = "userCache",key = "#id") //#id 代表变量名为id的参数
            @DeleteMapping("/{id}")
            public void delete(@PathVariable Long id){
                userService.removeById(id);
            }
        
  • ②、使用Postman进行功能测试

    • 测试缓存的删除,先访问save方法任意次,保存n条数据到数据库的同时,也保存到缓存中,最终可以通过debug看到缓存中的数据信息,然后可以通过Postman方法delete方法,进行缓存的删除
    • 删除数据的时候,通过debug可以看到已经缓存的4条数据
    • 当执行完delete操作后,我们再保存一条数据,在保存的时候debug查看之前的缓存是否已经被删除
  • ③、在update方法上加注解@CacheEvict

    • 在更新数据之后,数据库的数据已经发生了变更,我们需要将缓存中对应的数据删除掉,避免出现数据库数据与缓存数据不一致的情况

      •     //@CacheEvict(value = "userCache",key = "#p0.id")   //第一个参数的id属性
            //@CacheEvict(value = "userCache",key = "#user.id") //参数名为user参数的id属性
            //@CacheEvict(value = "userCache",key = "#root.args[0].id") //第一个参数的id属性
            @CacheEvict(value = "userCache",key = "#result.id")         //返回值的id属性
            @PutMapping
            public User update(@RequestBody  User user){
                userService.updateById(user);
                return user;
            }
        
    • 加上注解之后,重启服务,然后使用Postman进行测试,测试步骤和方法跟上述①、②差不多,这里就不再演示

4.3.4、@Cacheable注解

  • @Cacheable注解说明
    • 作用
      • 在方法执行前,Spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放入到缓存中
    • value
      • 缓存的名称,每个缓存名称下面可以有多个key
    • key
      • 缓存的key,支持Spring的表达式语言SPEL语法
  • ①、在getById方法上加@Cacheable注解

    •     @Cacheable(value = "user",key = "#id")
          @GetMapping("/{id}")
          public User getById(@PathVariable Long id){
              User user = userService.findById(id);
              return user;
          }
      
  • ②、使用Postman进行功能测试

    • 重启服务,然后通过debug断点跟踪程序执行;可以发现,第一次访问,会请求Controller的方法,查询数据库。后面再查询相同的id,就直接获取到数据库,不用再查询数据库了,就说明缓存已经生效
      • 第一次查询
      • 第二次查询
    • 在测试的时候,查询一个数据库中不存在的id值,第一次查询缓存中没有,也会查询数据库。第二次查询的时候,会发现,不再查询数据库了,而是直接返回,那也就是说如果根据id没有查询到数据,那么会自动缓存一个null值,可以通过debug进行验证一下
      • 第一次查询
      • 第二次查询
    • 这时候就会出现一个问题,能不能查询到的值不为null值的时候再进行缓存,如果为null值,则不进行缓存呢?
  • ③、缓存非null值

    • @Cacheable注解中,提供了两个属性分别为:conditionunless

      • condition
        • 表示满足什么条件,再进行缓存
      • unless
        • 表示满足条件则不缓存,与上述的condition是反向的
    • 具体实现方法如下所示

      •     /**
             * 注意: @Cacheable把方法的返回值缓存起来, 即使方法返回值为null也会被缓存,如果需要改变这个结果:
             *          condition : 符合指定条件则缓存,这个属性不建议使用,因为condition这个属性不能使用result。
             *          unless : 不符合指定条件则缓存
             */
            // @Cacheable 执行方法前先判断缓存是否存在指定id的user,
            // 如果存在不会执行方法,直接返回缓存中数据即可。如果不存在才会返回缓存数据
            @Cacheable(value = "user",key = "#id",unless = "#result==null")
            @GetMapping("/{id}")
            public User getById(@PathVariable Long id){
                User user = userService.findById(id);
                return user;
            }
        
    • 这里这能使用unless,因为condition属性无法获取到结果#result

4.4.、Spring Cache集成Redis

  • 在使用上述默认的ConcurrentHashMap做缓存时,服务重启之后,之前缓存的数据就全部丢失了,操作起来并不友好。在项目中使用,我们会选择使用redis来做缓存,主要需要操作以下几步

    • ①、添加依赖

      •        <!--spring cache依赖导入-->
               <dependency>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-cache</artifactId>
               </dependency>
        
               <!--spring data redis 导入-->
               <dependency>
                   <groupId>org.springframework.boot</groupId>
                   <artifactId>spring-boot-starter-data-redis</artifactId>
               </dependency>
        
    • ②、配置application.yaml配置文件(Spring层级下)

      •   redis:
            host: 127.0.0.1
            database: 2
          cache:
            redis:
              time-to-live: 1800000   #单位毫秒,设置缓存过期时间,可选
        
    • ③、测试

五、缓存套餐数据

5.1、思路分析

  • 前面已经实现了移动端套餐查看功能,对应的服务端方法为SetmealController的list方法,此方法会根据前端提交的查询条件进行数据库查询操作
  • 在高并发的情况下,频繁地查询数据库会导致系统性能下降,服务端响应时间增长。现在需要对此方法进行缓存优化,提高系统的性能

5.2、代码改造

  • ①、导入SpringCache和Redis相关的maven坐标(spring data redis之前已经导入过了)

    • <!--spring cache依赖-->
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-cache</artifactId>
              </dependency>
      
  • ②、在application.yml配置文件中配置缓存数据的过期时间

    •   cache:
          redis:
            time-to-live: 1800000 # 设置缓存数据过期时间
      
  • ③、在启动类上加入@EnableCaching注解,开启缓存注解功能

    • package com.coolman;
      
      import org.mybatis.spring.annotation.MapperScan;
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.boot.web.servlet.ServletComponentScan;
      import org.springframework.cache.annotation.EnableCaching;
      import org.springframework.transaction.annotation.EnableTransactionManagement;
      
      @SpringBootApplication
      @MapperScan(basePackages = "com.coolman.mapper")
      @ServletComponentScan(basePackages = "com.coolman.filters")
      @EnableTransactionManagement //开启对事务管理的支持
      @EnableCaching
      public class ReggieApplication {
          public static void main(String[] args) {
              SpringApplication.run(ReggieApplication.class, args);
          }
      }
      
      
  • ④、在SetmealServiceImplselectByCategoryIdAndStatus方法上加入@Cacheable注解

    • 在进行套餐数据查询时,需要根据分类ID和套餐的状态进行查询,所以在缓存数据的时候,可以将套餐分类id和套餐状态组合起来作为key,如下所示

      • 1627182182_1(1627182182是分类id,1是状态)
    •     @Override
          @Cacheable(value = "setmeal", key = "#categoryId + '_' + #status")
          public List<SetMeal> selectByCategoryIdAndStatus(Long categoryId, Integer status) {
              return setMealMapper.selectByCategoryIdAndStatus(categoryId, status);
          }
      
  • ⑤、在SetmealServiceImpl的数据变更方法上加入@CacheEvict注解

    • 为了保证数据库中数据与缓存数据的一致性,在添加套餐或者删除套餐数据之后,需要清空当前套餐缓存的全部数据

    • 那么@CacheEvict注解如何清除某一份缓存下所有的数据呢?这里可以指定@CacheEvict中的一个属性allEnties,将其设置为true即可,其含义为setmeal名称空间下面的所有key都删除

    •     @CacheEvict(value = "setmeal", allEntries = true)   // allEntries = true 代表了setmeal名称空间下面的所有key都删除
          public void update(SetMealDto setMealDto) {
              // 补全数据,更新时间
              setMealDto.setUpdateTime(LocalDateTime.now());
      
              // 更新setmeal表的数据
              setMealMapper.updateByIds(setMealDto, new Long[]{setMealDto.getId()});
      
              // 更新setmeal_dish表的数据
              // 先删除,再修改
              // 原因: 前端返回的数据是一个集合,有多个id,同时其中的数据也不是固定不变的,且是一对多关系,处理起来非常麻烦
      
              // 给setmeal_dish补全数据
              List<SetMealDish> setmealDishes = setMealDto.getSetmealDishes();
              for (SetMealDish setmealDish : setmealDishes) {
                  // 设置创建人和创建时间
                  setmealDish.setCreateUser(setMealDto.getCreateUser());
                  setmealDish.setCreateTime(setMealDto.getCreateTime());
      
                  // 设置更新时间和更新人
                  setmealDish.setUpdateUser(setMealDto.getUpdateUser());
                  setmealDish.setUpdateTime(LocalDateTime.now());
      
                  // 设置setmeal_id
                  setmealDish.setSetmealId(setMealDto.getId().toString());
      
                  // 设置sort
                  setmealDish.setSort(0);
              }
              // 根据id批量删除数据
              ArrayList<String> ids = new ArrayList<>();
              ids.add(setMealDto.getId().toString());
              setMealDishMapper.deleteByIds(ids);
      
              // 批量插入数据
              setMealDishMapper.batchInsert(setmealDishes);
      
          }
      
    • 上述代码只是修改功能的一个方法,其他有数据变更的操作,一般都要清理缓存,以保证数据库的数据和缓存的数据一致

5.3、功能测试

  • 代码编写完成之后,重启工程,然后访问后台管理系统,对套餐数据进行新增以及删除, 然后通过Redis的图形化界面工具,查看Redis中的套餐缓存是否已经被删除
  • ①、第一次查询套餐列表,查看Redis中是否存储相对应的数据
  • ②、第二次查询套餐列表,查看服务端终端输出的是否有SQL语句,验证是否是从Redis中读取数据
  • ③、执行数据变更操作(这里测试使用添加功能),查看Redis中的缓存数据是否删除
    • 这里有部分dish值出现null字符的原因是之前在没单纯使用RedisTemplate对象的时候没有删除测试代码,所以不管传入的status值是否为null值,其都会存储到缓存中,不过在这里无伤大雅,仅作为测试,在实际应用场景中,非常不建议同时使用RedisTemplate和Spring redis data
  • 到这里,项目的缓存优化结束!