0


基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(十一)

加入redis缓存

1. 缓存菜品

1.1 问题说明

用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大。

在这里插入图片描述

结果:系统响应慢、用户体验差

1.2 实现思路

通过Redis来缓存菜品数据,减少数据库查询操作。

在这里插入图片描述

缓存逻辑分析:

  • 每个分类下的菜品保存一份缓存数据
  • 数据库中菜品数据有变更时清理缓存数据

1.3 代码开发

修改用户端接口 DishController 的 list 方法,加入缓存处理逻辑:

@AutowiredprivateRedisTemplate redisTemplate;/**
     * 根据分类id查询菜品
     *
     * @param categoryId
     * @return
     */@GetMapping("/list")@ApiOperation("根据分类id查询菜品")publicResult<List<DishVO>>list(Long categoryId){//构造redis中的key,规则:dish_分类idString key ="dish_"+ categoryId;//查询redis中是否存在菜品数据List<DishVO> list =(List<DishVO>) redisTemplate.opsForValue().get(key);if(list !=null&& list.size()>0){//如果存在,直接返回,无须查询数据库returnResult.success(list);}Dish dish =newDish();
        dish.setCategoryId(categoryId);
        dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品//如果不存在,查询数据库,将查询到的数据放入redis中
        list = dishService.listWithFlavor(dish);
        redisTemplate.opsForValue().set(key, list);returnResult.success(list);}

为了保证数据库Redis中的数据保持一致,修改管理端接口 DishController 的相关方法,加入清理缓存逻辑。

需要改造的方法:

  • 新增菜品
  • 修改菜品
  • 批量删除菜品
  • 起售、停售菜品

抽取清理缓存的方法:

在管理端DishController中添加

@AutowiredprivateRedisTemplate redisTemplate;/**
     * 清理缓存数据
     * @param pattern
     */privatevoidcleanCache(String pattern){Set keys = redisTemplate.keys(pattern);
        redisTemplate.delete(keys);}

调用清理缓存的方法,保证数据一致性:

1). 新增菜品优化

/**
     * 新增菜品
     *
     * @param dishDTO
     * @return
     */@PostMapping@ApiOperation("新增菜品")publicResultsave(@RequestBodyDishDTO dishDTO){
        log.info("新增菜品:{}", dishDTO);
        dishService.saveWithFlavor(dishDTO);//清理缓存数据String key ="dish_"+ dishDTO.getCategoryId();cleanCache(key);returnResult.success();}

2). 菜品批量删除优化

/**
     * 菜品批量删除
     *
     * @param ids
     * @return
     */@DeleteMapping@ApiOperation("菜品批量删除")publicResultdelete(@RequestParamList<Long> ids){
        log.info("菜品批量删除:{}", ids);
        dishService.deleteBatch(ids);//将所有的菜品缓存数据清理掉,所有以dish_开头的keycleanCache("dish_*");returnResult.success();}

3). 修改菜品优化

/**
     * 修改菜品
     *
     * @param dishDTO
     * @return
     */@PutMapping@ApiOperation("修改菜品")publicResultupdate(@RequestBodyDishDTO dishDTO){
        log.info("修改菜品:{}", dishDTO);
        dishService.updateWithFlavor(dishDTO);//将所有的菜品缓存数据清理掉,所有以dish_开头的keycleanCache("dish_*");returnResult.success();}

4). 菜品起售停售优化

/**
     * 菜品起售停售
     *
     * @param status
     * @param id
     * @return
     */@PostMapping("/status/{status}")@ApiOperation("菜品起售停售")publicResult<String>startOrStop(@PathVariableInteger status,Long id){
        dishService.startOrStop(status, id);//将所有的菜品缓存数据清理掉,所有以dish_开头的keycleanCache("dish_*");returnResult.success();}

1.4 功能测试

1). 加入缓存

当第一次查询某个分类的菜品时,会从数据为中进行查询,同时将查询的结果存储到Redis中,在后绪的访问,若查询相同分类的菜品时,直接从Redis缓存中查询,不再查询数据库。
查看控制台sql:有查询语句,说明是从数据库中进行查询,查看Redis中的缓存数据:说明缓存成功,再次访问控制台没有sql,说明是从缓存中查询。

2). 修改操作

当在后台修改菜品数据时,为了保证Redis缓存中的数据和数据库中的数据时刻保持一致,当修改后,需要清空对应的缓存数据。用户再次访问时,还是先从数据库中查询,同时再把查询的结果存储到Redis中,这样,就能保证缓存和数据库的数据保持一致。

进入后台:修改任意一个菜品,当前分类的菜品数据已在Redis中缓存。修改完成后查看Redis中的缓存数据发现没有,说明修改时,已清空缓存。用户再次访问同一个菜品分类时,需要先查询数据库,再把结果同步到Redis中,保证了两者数据一致性。

2. 缓存套餐

2.1 Spring Cache

2.1.1 介绍

Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。

Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如:

  • EHCache
  • Caffeine
  • Redis(常用)

起步依赖:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId><version>2.7.3</version></dependency>
2.1.2 常用注解

在SpringCache中提供了很多缓存操作的注解,常见的是以下的几个:
注解****说明@EnableCaching开启缓存注解功能,通常加在启动类上@Cacheable在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中@CachePut将方法的返回值放到缓存中@CacheEvict将一条或多条数据从缓存中删除
在spring boot项目中,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在启动类上使用@EnableCaching开启缓存支持即可。

例如,使用Redis作为缓存技术,只需要导入Spring data Redis的maven坐标即可。

2.1.3 入门案例

引导类上加@EnableCaching:

@Slf4j@SpringBootApplication@EnableCaching//开启缓存注解功能publicclassCacheDemoApplication{publicstaticvoidmain(String[] args){SpringApplication.run(CacheDemoApplication.class,args);
        log.info("项目启动成功...");}}

2). @CachePut注解

@CachePut 说明:

​ 作用: 将方法返回值,放入缓存

​ value: 缓存的名称, 每个缓存名称下面可以有很多key

​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

在save方法上加注解@CachePut

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

/**
    * CachePut:将方法返回值放入缓存
    * value:缓存的名称,每个缓存名称下面可以有多个key
    * key:缓存的key
    */@PostMapping@CachePut(value ="userCache", key ="#user.id")//key的生成:userCache::1publicUsersave(@RequestBodyUser user){
        userMapper.insert(user);return user;}

说明:key的写法如下

#user.id : #user指的是方法形参的名称, id指的是user的id属性 , 也就是使用user的id属性作为key ;

#result.id : #result代表方法返回值,该表达式 代表以返回对象的id属性作为key ;

#p0.id:#p0指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;

#a0.id:#a0指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;

#root.args[0].id:#root.args[0]指的是方法中的第一个参数,id指的是第一个参数的id属性,也就是使用第一个参数的id属性作为key ;

3). @Cacheable注解

@Cacheable 说明:

​ 作用: 在方法执行前,spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,调用方法并将方法返回值放到缓存中

​ value: 缓存的名称,每个缓存名称下面可以有多个key

​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

在getById上加注解@Cacheable

/**
    * Cacheable:在方法执行前spring先查看缓存中是否有数据,如果有数据,则直接返回缓存数据;若没有数据,      *调用方法并将方法返回值放到缓存中
    * value:缓存的名称,每个缓存名称下面可以有多个key
    * key:缓存的key
    */@GetMapping@Cacheable(cacheNames ="userCache",key="#id")publicUsergetById(Long id){User user = userMapper.getById(id);return user;}

4). @CacheEvict注解

@CacheEvict 说明:

​ 作用: 清理指定缓存

​ value: 缓存的名称,每个缓存名称下面可以有多个key

​ key: 缓存的key ----------> 支持Spring的表达式语言SPEL语法

在 delete 方法上加注解@CacheEvict

@DeleteMapping@CacheEvict(cacheNames ="userCache",key ="#id")//删除某个key对应的缓存数据publicvoiddeleteById(Long id){
        userMapper.deleteById(id);}@DeleteMapping("/delAll")@CacheEvict(cacheNames ="userCache",allEntries =true)//删除userCache下所有的缓存数据publicvoiddeleteAll(){
        userMapper.deleteAll();}

2.2 具体实现思路

实现步骤:

1). 导入Spring Cache和Redis相关maven坐标

2). 在启动类上加入@EnableCaching注解,开启缓存注解功能

3). 在用户端接口SetmealController的 list 方法上加入@Cacheable注解

4). 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解

2.3 代码开发

按照上述实现步骤:

1). 导入Spring Cache和Redis相关maven坐标(已实现)

<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). 在启动类上加入@EnableCaching注解,开启缓存注解功能

packagecom.sky;@SpringBootApplication@EnableTransactionManagement//开启注解方式的事务管理@Slf4j@EnableCachingpublicclassSkyApplication{publicstaticvoidmain(String[] args){SpringApplication.run(SkyApplication.class, args);
        log.info("server started");}}

3). 在用户端接口SetmealController的 list 方法上加入@Cacheable注解

/**
     * 条件查询
     *
     * @param categoryId
     * @return
     */@GetMapping("/list")@ApiOperation("根据分类id查询套餐")@Cacheable(cacheNames ="setmealCache",key ="#categoryId")//key: setmealCache::100publicResult<List<Setmeal>>list(Long categoryId){Setmeal setmeal =newSetmeal();
        setmeal.setCategoryId(categoryId);
        setmeal.setStatus(StatusConstant.ENABLE);List<Setmeal> list = setmealService.list(setmeal);returnResult.success(list);}

4). 在管理端接口SetmealController的 save、delete、update、startOrStop等方法上加入CacheEvict注解

/**
     * 新增套餐
     *
     * @param setmealDTO
     * @return
     */@PostMapping@ApiOperation("新增套餐")@CacheEvict(cacheNames ="setmealCache",key ="#setmealDTO.categoryId")//key: setmealCache::100publicResultsave(@RequestBodySetmealDTO setmealDTO){
        setmealService.saveWithDish(setmealDTO);returnResult.success();}/**
     * 批量删除套餐
     *
     * @param ids
     * @return
     */@DeleteMapping@ApiOperation("批量删除套餐")@CacheEvict(cacheNames ="setmealCache",allEntries =true)publicResultdelete(@RequestParamList<Long> ids){
        setmealService.deleteBatch(ids);returnResult.success();}/**
     * 修改套餐
     *
     * @param setmealDTO
     * @return
     */@PutMapping@ApiOperation("修改套餐")@CacheEvict(cacheNames ="setmealCache",allEntries =true)publicResultupdate(@RequestBodySetmealDTO setmealDTO){
        setmealService.update(setmealDTO);returnResult.success();}/**
     * 套餐起售停售
     *
     * @param status
     * @param id
     * @return
     */@PostMapping("/status/{status}")@ApiOperation("套餐起售停售")@CacheEvict(cacheNames ="setmealCache",allEntries =true)publicResultstartOrStop(@PathVariableInteger status,Long id){
        setmealService.startOrStop(status, id);returnResult.success();}

后记
👉👉💕💕美好的一天,到此结束,下次继续努力!欲知后续,请看下回分解,写作不易,感谢大家的支持!! 🌹🌹🌹


本文转载自: https://blog.csdn.net/m0_59230408/article/details/134427520
版权归原作者 失重外太空. 所有, 如有侵权,请联系我们删除。

“基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(十一)”的评论:

还没有评论