一、菜品缓存
减少查询数据库的次数,优化性能
客户端:
package com.sky.controller.user;import com.sky.constant.StatusConstant;
import com.sky.entity.Dish;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {@Autowiredprivate DishService dishService;@Autowiredprivate RedisTemplate redisTemplate;/*** 根据分类id查询菜品** @param categoryId* @return*/@GetMapping("/list")@ApiOperation("根据分类id查询菜品")public Result<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) {// 如果存在,直接返回,无需查询数据库return Result.success(list);}Dish dish = new Dish();dish.setCategoryId(categoryId);dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品// 如果不存在,查询数据库,将查询到的数据放入redis中list = dishService.listWithFlavor(dish);redisTemplate.opsForValue().set(key, list);return Result.success(list);}}
管理端:
package com.sky.controller.admin;import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Dish;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.DishService;
import com.sky.vo.DishVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.*;import java.util.List;
import java.util.Set;/*** 菜品管理*/
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {@Autowiredprivate DishService dishService;@Autowiredprivate RedisTemplate redisTemplate;/*** 新增菜品* @param dishDTO* @return*/@PostMapping@ApiOperation("新增菜品")public Result save(@RequestBody DishDTO dishDTO) {log.info("新增菜品:{}", dishDTO);dishService.saveWithFlavor(dishDTO);// 清理缓存数据String key = "dish_" + dishDTO.getCategoryId();clearCache(key);return Result.success();}/*** 菜品分页查询* @param dishPageQueryDTO* @return*/@GetMapping("/page")@ApiOperation("菜品分页查询")public Result<PageResult> page(DishPageQueryDTO dishPageQueryDTO) {log.info("菜品分页查询:{}", dishPageQueryDTO);PageResult pageResult = dishService.pageQuery(dishPageQueryDTO);return Result.success(pageResult);}/*** 菜品批量删除* @param ids* @return*/@DeleteMapping@ApiOperation("菜品批量删除")public Result delete(@RequestParam List<Long> ids) {log.info("菜品批量删除:{}", ids);dishService.deleteBatch(ids);// 将所以的菜品缓存数据清除,所有以dish_开头的keyclearCache("dish_*");return Result.success();}/*** 根据id查询菜品* @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询菜品")public Result<DishVO> getById(@PathVariable Long id) {log.info("根据id查询菜品:{}", id);DishVO dishVO = dishService.getByIdWithFlavor(id);return Result.success(dishVO);}/*** 修改菜品* @param dishDTO* @return*/@PutMapping@ApiOperation("修改菜品")public Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品:{}", dishDTO);dishService.updateWithFlavor(dishDTO);// 将所以的菜品缓存数据清除,所有以dish_开头的keyclearCache("dish_*");return Result.success();}/*** 菜品起售停售* @param status* @param id* @return*/@PostMapping("/status/{status}")@ApiOperation("菜品起售停售")public Result<String> startOrStop(@PathVariable Integer status, Long id) {dishService.startOrStop(status, id);// 将所以的菜品缓存数据清除,所有以dish_开头的keyclearCache("dish_*");return Result.success();}/*** 根据分类id查询菜品* @param categoryId* @return*/@GetMapping("/list")@ApiOperation("根据分类id查询菜品")public Result<List<Dish>> list(Long categoryId) {List<Dish> list = dishService.list(categoryId);return Result.success(list);}/*** 清理缓存数据* @param pattern*/private void clearCache(String pattern) {Set keys = redisTemplate.keys(pattern);redisTemplate.delete(keys);}
}
二、Spring Cache
1. 概述
介绍
Spring Cache是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。
Spring Cache提供了一层抽象,底层可以切换不同的缓存实现,例如:
- EHCache
- Caffeine
- Redis
常用注解
注解 | 说明 |
@EnableCaching | 开启缓存注解功能,通常加在启动类上 |
@Cacheable | 在方法执行前先查询缓存中是否有数据,如果有数据,则直接返回缓存数据;如果没有缓存数据,调用方法并将方法返回值放到缓存中 |
@CachePut | 将方法的返回值放到缓存中 |
@CacheEvict | 将一条或多条数据从缓存中删除 |
2. 入门小案例
具体参见day07/springcache-demo项目
- 导入Maven坐标
<!-- 缓存--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId><version>2.7.3</version></dependency><!-- 缓存中间件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
- 创建数据库
CREATE DATABASE IF NOT EXISTS spring_cache_demo;
USE spring_cache_demo;DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (`id` bigint NOT NULL AUTO_INCREMENT,`name` varchar(45) DEFAULT NULL,`age` int DEFAULT NULL,PRIMARY KEY (`id`)
);
- 修改配置文件里的数据库连接密码和redis连接密码以及使用的缓存数据库
- 添加注解
①启动类
- UserController——使用注解
package com.itheima.controller;import com.itheima.entity.User;
import com.itheima.mapper.UserMapper;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/user")
@Slf4j
@Api(tags = "用户相关接口")
public class UserController {@Autowiredprivate UserMapper userMapper;@PostMapping// 将方法的返回值放到缓存中// 如果使用Spring Cache缓存数据,key的生成:cacheNames::key@CachePut(cacheNames = "userCache", key = "#user.id") // 从参数获取key// @CachePut(cacheNames = "userCache", key = "#result.id") // 从返回值获取key// @CachePut(cacheNames = "userCache", key = "#p0.id") // 从第一个参数获取// @CachePut(cacheNames = "userCache", key = "#a0.id") // 从第一个参数获取// @CachePut(cacheNames = "userCache", key = "#root.args[0].id") // 从第一个参数获取public User save(@RequestBody User user){userMapper.insert(user);return user;}@DeleteMapping@CacheEvict(cacheNames = "userCache", key = "#id")public void deleteById(Long id){userMapper.deleteById(id);}@DeleteMapping("/delAll")@CacheEvict(cacheNames = "userCache", allEntries = true)public void deleteAll(){userMapper.deleteAll();}@GetMapping@Cacheable(cacheNames = "userCache", key = "#id")public User getById(Long id){User user = userMapper.getById(id);return user;}}
3. spEL的简单介绍
SpEL(Spring Expression Language)是Spring框架中的表达式语言,用于在Spring应用程序中进行动态计算和引用。SpEL提供了一种类似于传统编程语言的表达式语法,可以在运行时对对象图进行访问和操作。
SpEL主要用于以下方面:
- 在Spring配置文件中引用bean的属性值;
- 在注解中引用bean的属性值;
- 在Spring的注解驱动开发中进行条件判断和动态配置;
- 在Spring Security中进行权限控制;
- 在Spring Data中进行查询定义。
SpEL支持各种表达式操作,包括算术运算、逻辑运算、关系运算、正则表达式匹配、集合操作等。通过SpEL,可以使Spring应用程序更加灵活和动态,减少硬编码的情况,提高代码的的可维护性和可读性。
示例1:
@Value("#systemProperties['java.home']}")
private String javaHome;
在这个示例中,@Value注解中使用SpEL表达式#systemProperties['java.home']}来获取Java的安装路径,并将其赋值给javaHome变量。
示例2:
@PostMapping// 将方法的返回值放到缓存中// 如果使用Spring Cache缓存数据,key的生成:cacheNames::key// @CachePut(cacheNames = "userCache", key = "#user.id") // 从参数获取key// @CachePut(cacheNames = "userCache", key = "#result.id") // 从返回值获取key// @CachePut(cacheNames = "userCache", key = "#p0.id") // 从第一个参数获取// @CachePut(cacheNames = "userCache", key = "#a0.id") // 从第一个参数获取@CachePut(cacheNames = "userCache", key = "#root.args[0].id") // 从第一个参数获取public User save(@RequestBody User user){userMapper.insert(user);return user;}
三、作业代码——删除购物车中一个商品
1. ShoppingCartController
/*** 删除购物车中一个商品* @param shoppingCartDTO* @return*/@PostMapping("/sub")@ApiOperation("删除购物车中一个商品")public Result sub(@RequestBody ShoppingCartDTO shoppingCartDTO) {log.info("删除购物车中一个商品,商品:{}", shoppingCartDTO);shoppingCartService.subShoppingCart(shoppingCartDTO);return Result.success();}
2. ShoppingCartService
/*** 删除购物车中一个商品* @param shoppingCartDTO*/void subShoppingCart(ShoppingCartDTO shoppingCartDTO);
3. ShoppingCartServiceImpl
/*** 删除购物车中一个商品* @param shoppingCartDTO*/@Overridepublic void subShoppingCart(ShoppingCartDTO shoppingCartDTO) {ShoppingCart shoppingCart = new ShoppingCart();BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);// 设置查询条件,查询当前登录用户的购物车数据shoppingCart.setUserId(BaseContext.getCurrentId());List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);if(list != null && list.size() > 0) {shoppingCart = list.get(0);Integer number = shoppingCart.getNumber();if(number == 1) {// 当前商品在购物车中的份数为1,直接删除当前记录shoppingCartMapper.deleteById(shoppingCart.getId());} else {// 当前商品在购物车中的份数不为1,修改份数即可shoppingCart.setNumber(shoppingCart.getNumber() - 1);shoppingCartMapper.updateNumberById(shoppingCart);}}
4. ShoppingCartMapper
/*** 根据id删除购物车数据* @param id*/@Delete("delete from shopping_cart where id = #{id}")void deleteById(Long id);