一.Redis緩存
修改用戶端接口 DishController 的 list 方法,加入緩存處理邏輯:
@Autowired
private RedisTemplate redisTemplate;
/**
* 根據(jù)分類id查詢菜品
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根據(jù)分類id查詢菜品")
public Result<List<DishVO>> list(Long categoryId) {
//構(gòu)造redis中的key,規(guī)則:dish_分類id
String key = "dish_" + categoryId;
//查詢r(jià)edis中是否存在菜品數(shù)據(jù)
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if(list != null && list.size() > 0){
//如果存在,直接返回,無(wú)須查詢數(shù)據(jù)庫(kù)
return Result.success(list);
}
////////////////////////////////////////////////////////
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查詢起售中的菜品
//如果不存在,查詢數(shù)據(jù)庫(kù),將查詢到的數(shù)據(jù)放入redis中
list = dishService.listWithFlavor(dish);
////////////////////////////////////////////////////////
redisTemplate.opsForValue().set(key, list);
return Result.success(list);
}
為了保證數(shù)據(jù)庫(kù)和Redis中的數(shù)據(jù)保持一致,修改管理端接口 DishController 的相關(guān)方法,加入清理緩存邏輯。
需要改造的方法:
- 新增菜品
- 修改菜品
- 批量刪除菜品
- 起售、停售菜品
抽取清理緩存的方法:
在管理端DishController中添加
@Autowired
private RedisTemplate redisTemplate;
/**
* 清理緩存數(shù)據(jù)
* @param pattern
*/
private void cleanCache(String pattern){
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
調(diào)用清理緩存的方法,保證數(shù)據(jù)一致性:
1). 新增菜品優(yōu)化
/**
* 新增菜品
*
* @param dishDTO
* @return
*/
@PostMapping
@ApiOperation("新增菜品")
public Result save(@RequestBody DishDTO dishDTO) {
log.info("新增菜品:{}", dishDTO);
dishService.saveWithFlavor(dishDTO);
//清理緩存數(shù)據(jù)
String key = "dish_" + dishDTO.getCategoryId();
cleanCache(key);
return Result.success();
}
2). 菜品批量刪除優(yōu)化
/**
* 菜品批量刪除
*
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("菜品批量刪除")
public Result delete(@RequestParam List<Long> ids) {
log.info("菜品批量刪除:{}", ids);
dishService.deleteBatch(ids);
//將所有的菜品緩存數(shù)據(jù)清理掉,所有以dish_開(kāi)頭的key
cleanCache("dish_*");
return Result.success();
}
**3). 修改菜品優(yōu)化 **
/**
* 修改菜品
*
* @param dishDTO
* @return
*/
@PutMapping
@ApiOperation("修改菜品")
public Result update(@RequestBody DishDTO dishDTO) {
log.info("修改菜品:{}", dishDTO);
dishService.updateWithFlavor(dishDTO);
//將所有的菜品緩存數(shù)據(jù)清理掉,所有以dish_開(kāi)頭的key
cleanCache("dish_*");
return Result.success();
}
4). 菜品起售停售優(yōu)化
/**
* 菜品起售停售
*
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("菜品起售停售")
public Result<String> startOrStop(@PathVariable Integer status, Long id) {
dishService.startOrStop(status, id);
//將所有的菜品緩存數(shù)據(jù)清理掉,所有以dish_開(kāi)頭的key
cleanCache("dish_*");
return Result.success();
}
二、Spring Cache
Spring Cache 是一個(gè)框架,實(shí)現(xiàn)了基于注解的緩存功能,只需要簡(jiǎn)單地加一個(gè)注解,就能實(shí)現(xiàn)緩存功能。
提供了一層抽象,底層可以切換不同的緩存實(shí)現(xiàn),例如:
- EHCache
- Caffeine
- Redis(常用)
起步依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId> <version>2.7.3</version>
</dependency>
在SpringCache中提供了很多緩存操作的注解,常見(jiàn)的是以下的幾個(gè):
| 注解 | 說(shuō)明 |
|---|---|
| @EnableCaching | 開(kāi)啟緩存注解功能,通常加在啟動(dòng)類上 |
| @Cacheable | 在方法執(zhí)行前先查詢緩存中是否有數(shù)據(jù),如果有數(shù)據(jù),則直接返回緩存數(shù)據(jù);如果沒(méi)有緩存數(shù)據(jù),調(diào)用方法并將方法返回值放到緩存中 |
| @CachePut | 將方法的返回值放到緩存中 |
| @CacheEvict | 將一條或多條數(shù)據(jù)從緩存中刪除 |
在spring boot項(xiàng)目中,使用緩存技術(shù)只需在項(xiàng)目中導(dǎo)入相關(guān)緩存技術(shù)的依賴包,并在啟動(dòng)類上使用@EnableCaching開(kāi)啟緩存支持即可。
例如,使用Redis作為緩存技術(shù),只需要導(dǎo)入Spring data Redis的maven坐標(biāo)即可。
1)引導(dǎo)類上加@EnableCaching:
package com.itheima;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@Slf4j
@SpringBootApplication
@EnableCaching//開(kāi)啟緩存注解功能
public class CacheDemoApplication {
public static void main(String[] args) {
SpringApplication.run(CacheDemoApplication.class,args);
log.info("項(xiàng)目啟動(dòng)成功...");
}
}
2). @CachePut注解
@CachePut 說(shuō)明:
作用: 將方法返回值,放入緩存
value: 緩存的名稱, 每個(gè)緩存名稱下面可以有很多key
key: 緩存的key ----------> 支持Spring的表達(dá)式語(yǔ)言SPEL語(yǔ)法
在save方法上加注解@CachePut
當(dāng)前UserController的save方法是用來(lái)保存用戶信息的,我們希望在該用戶信息保存到數(shù)據(jù)庫(kù)的同時(shí),也往緩存中緩存一份數(shù)據(jù),我們可以在save方法上加上注解 @CachePut,用法如下:
/**
* CachePut:將方法返回值放入緩存
* value:緩存的名稱,每個(gè)緩存名稱下面可以有多個(gè)key
* key:緩存的key
*/
@PostMapping
@CachePut(value = "userCache", key = "#user.id")//key的生成:userCache::1
public User save(@RequestBody User user){
userMapper.insert(user);
return user;
}
說(shuō)明:key的寫(xiě)法如下
#user.id: #user指的是方法形參的名稱, id指的是user的id屬性 , 也就是使用user的id屬性作為key ;
#result.id: #result代表方法返回值,該表達(dá)式 代表以返回對(duì)象的id屬性作為key ;
#p0.id:#p0指的是方法中的第一個(gè)參數(shù),id指的是第一個(gè)參數(shù)的id屬性,也就是使用第一個(gè)參數(shù)的id屬性作為key ;
#a0.id:#a0指的是方法中的第一個(gè)參數(shù),id指的是第一個(gè)參數(shù)的id屬性,也就是使用第一個(gè)參數(shù)的id屬性作為key ;
#root.args[0].id:#root.args[0]指的是方法中的第一個(gè)參數(shù),id指的是第一個(gè)參數(shù)的id屬性,也就是使用第一個(gè)參數(shù)的id屬性作為key ;
啟動(dòng)服務(wù),通過(guò)swagger接口文檔測(cè)試,訪問(wèn)UserController的save()方法
因?yàn)閕d是自增,所以不需要設(shè)置id屬性
3). @Cacheable注解
@Cacheable 說(shuō)明:
作用: 在方法執(zhí)行前,spring先查看緩存中是否有數(shù)據(jù),如果有數(shù)據(jù),則直接返回緩存數(shù)據(jù);若沒(méi)有數(shù)據(jù),調(diào)用方法并將方法返回值放到緩存中
value: 緩存的名稱,每個(gè)緩存名稱下面可以有多個(gè)key
key: 緩存的key ----------> 支持Spring的表達(dá)式語(yǔ)言SPEL語(yǔ)法
在getById上加注解@Cacheable
@GetMapping
@Cacheable(cacheNames = "userCache",key="#id")
public User getById(Long id){
User user = userMapper.getById(id);
return user;
}
4). @CacheEvict注解
@CacheEvict 說(shuō)明:
作用: 清理指定緩存
value: 緩存的名稱,每個(gè)緩存名稱下面可以有多個(gè)key
key: 緩存的key ----------> 支持Spring的表達(dá)式語(yǔ)言SPEL語(yǔ)法
在 delete 方法上加注解@CacheEvict
@DeleteMapping
@CacheEvict(cacheNames = "userCache",key = "#id")//刪除某個(gè)key對(duì)應(yīng)的緩存數(shù)據(jù)
public void deleteById(Long id){
userMapper.deleteById(id);
}
@DeleteMapping("/delAll")
@CacheEvict(cacheNames = "userCache",allEntries = true)//刪除userCache下所有的緩存數(shù)據(jù)
public void deleteAll(){
userMapper.deleteAll();
}
項(xiàng)目使用
1). 導(dǎo)入Spring Cache和Redis相關(guān)maven坐標(biāo)(已實(shí)現(xiàn))
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
2). 在啟動(dòng)類上加入@EnableCaching注解,開(kāi)啟緩存注解功能
package com.sky;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement //開(kāi)啟注解方式的事務(wù)管理
@Slf4j
@EnableCaching
public class SkyApplication {
public static void main(String[] args) {
SpringApplication.run(SkyApplication.class, args);
log.info("server started");
}
}
3). 在用戶端接口SetmealController的 list 方法上加入@Cacheable注解
/**
* 條件查詢
*
* @param categoryId
* @return
*/
@GetMapping("/list")
@ApiOperation("根據(jù)分類id查詢套餐")
@Cacheable(cacheNames = "setmealCache",key = "#categoryId") //key: setmealCache::100
public Result<List<Setmeal>> list(Long categoryId) {
Setmeal setmeal = new Setmeal();
setmeal.setCategoryId(categoryId);
setmeal.setStatus(StatusConstant.ENABLE);
List<Setmeal> list = setmealService.list(setmeal);
return Result.success(list);
}
4). 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解
/**
* 新增套餐
*
* @param setmealDTO
* @return
*/
@PostMapping
@ApiOperation("新增套餐")
@CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")//key: setmealCache::100
public Result save(@RequestBody SetmealDTO setmealDTO) {
setmealService.saveWithDish(setmealDTO);
return Result.success();
}
/**
* 批量刪除套餐
*
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("批量刪除套餐")
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
public Result delete(@RequestParam List<Long> ids) {
setmealService.deleteBatch(ids);
return Result.success();
}
/**
* 修改套餐
*
* @param setmealDTO
* @return
*/
@PutMapping
@ApiOperation("修改套餐")
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
public Result update(@RequestBody SetmealDTO setmealDTO) {
setmealService.update(setmealDTO);
return Result.success();
}
/**
* 套餐起售停售
*
* @param status
* @param id
* @return
*/
@PostMapping("/status/{status}")
@ApiOperation("套餐起售停售")
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
public Result startOrStop(@PathVariable Integer status, Long id) {
setmealService.startOrStop(status, id);
return Result.success();
}