01-SpringBoot之SpringCache集成Redis

来源:https://www.cnblogs.com/OnlyOnYourself-lzw/archive/2022/08/02/16545009.html
-Advertisement-
Play Games

一、緩存機制的原理 一個系統在面向用戶使用的時候,當用戶的數量不斷增多,那麼請求次數也會不斷增多,當請求次數增多的時候,就會造成請求壓力,而我們當前的所有數據查詢都是從資料庫MySQL中直接查詢的,那麼就可能會產生如下問題 ==頻繁訪問資料庫,資料庫訪問壓力大,系統性能下降,用戶體驗差== 解決問題 ...


一、緩存機制的原理

  • 一個系統在面向用戶使用的時候,當用戶的數量不斷增多,那麼請求次數也會不斷增多,當請求次數增多的時候,就會造成請求壓力,而我們當前的所有數據查詢都是從資料庫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
  • 到這裡,項目的緩存優化結束!

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 網頁是一個頁面,網站是由多個網頁組成的! 我們在使用代碼編寫的時候能看到這樣的東西,具體內容如下,比較基礎: 認識SEO? SEO就是搜索引擎優化,作用就是你找查找相關內容時,能夠優先給你展示這個內容。 SEO三大標簽(一般由想乾人員提供): title(網頁標簽)、description(網頁描述 ...
  • 項目背景 我們的系統(一個 ToB 的 Web 單頁應用)前端單頁應用經過多年的迭代,目前已經累積有大幾十萬行的業務代碼,30+ 路由模塊,整體的代碼量和複雜度還是比較高的。 項目整體是基於 Vue + TypeScirpt,而構建工具,由於最早項目是經由 vue-cli 初始化而來,所以自然而然使 ...
  • 什麼時候精靈圖呢? 通常在渲染頁面的時候,需要伺服器向我們發送數據,但有的時候一個頁面需要多張圖時,伺服器就會處於連續發圖的工作狀態,但如果我們把需要的圖都放在一張圖上,這樣可以大大的減少服務的工作負擔,打個比喻。伺服器發一張圖是,工作流程是:找到圖片——讀取圖片——發送圖片,如果是發送5個圖片時, ...
  • 蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》 寫在開頭 我們都知道,經過多年的發展和無數Java開發者的不懈努力,Java已經由一門單純的電腦編程語言,逐漸演變成一套強大的以及仍在可持續發展中的技術體系平臺。 雖然,Java設計者們根據不同的技術規範,把 ...
  • 1. 登錄用戶數據獲取 登錄成功之後,在後續的業務邏輯中,開發者可能還需要獲取登錄成功的用戶對象,如果不使用任何安全管理框架,那麼可以將用戶信息保存在HttpSession中,以後需要的時候直接從HttpSession中獲取數據。在Spring Security中,用戶登錄信息本質上還是保存在 Ht ...
  • 24 類型標註 24.1 Python中的數據類型 在Python中有很多數據類型,比較常見如下所示: |整型 | 浮點型|字元串 | 列表|元組|字典|集合|布爾| | | | | | | | | | |int| float|str|list|tuple|dict|set|bool| 因Pytho ...
  • 目錄 一.簡介 二.效果演示 三.源碼下載 四.猜你喜歡 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 基礎 零基礎 OpenGL (ES) 學習路線推薦 : OpenGL (ES) 學習目錄 >> OpenGL ES 轉場 零基礎 O ...
  • 大家好,我是“良工說技術”。 今天給大家帶來的是springboot中的@ConditionalOnClass註解的用法。上次的@ConditionalOnBean註解還記得嗎? 一、@ConditionalOnClass註解初始 看下@CodidtionalOnClass註解的定義, 需要註意的有 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...