缓存菜品
问题说明
用户端小程序展示的数据是通过查询数据库获得,若访问量比较大,数据库访问压力变大
系统响应慢,用户体验差
实现思路
通过redis来缓存菜品数据,减少数据库查询操作
代码开发
//pom.xml导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
//application配置
spring:
redis:
host: ${sky.redis.host}
port: ${sky.redis.port}
password: ${sky.redis.password}
database: ${sky.redis.database}
//config
@Configuration
@Slf4j
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
log.info("开始创建redis模板对象...");
RedisTemplate redisTemplate = new RedisTemplate();
//设置redis的连接工厂对象
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置redis key的序列化器
redisTemplate.setKeySerializer(new StringRedisSerializer());
return redisTemplate;
}
}
//controller
public Result<List<DishVO>> list(Long categoryId) {
// 构造redis中的key,规则:dish_分类Id
String key = "dish_" + categoryId;
// 查询redis中是否存在菜品数据
List<DishVO> list = (List<DishVO>) redisTemplate.opsForValue().get(key);
if (list != null && list.size() > 0) {
// 如果存在,直接返回,无需查询数据库
return Result.success(list);
}
// 如果不存在,查询数据库,将查询到的数据放入redis中
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
list = dishService.listWithFlavor(dish);
// 放入redis
redisTemplate.opsForValue().set(key, list);
return Result.success(list);
}
}
清理缓存
//controller
@RestController
@RequestMapping("/admin/dish")
@Api(tags = "菜品相关接口")
@Slf4j
public class DishController {
@Autowired
private RedisTemplate redisTemplate;
// 清理缓存数据
String key = "dish_" + dishDTO.getCategoryId();
clearCache(key);
return Result.success();
}
/**
* 菜品批量删除
*
* @param ids
* @return
*/
@DeleteMapping
@ApiOperation("菜品批量删除")
public Result delete(@RequestParam List<Long> ids) {
log.info("菜品批量删除:{}", ids);
dishService.deleteBatch(ids);
// 将所有的菜品缓存数据清理掉,所有以dish_开头的key
clearCache("dish_*");
return Result.success();
}
/**
* 修改菜品
*
* @param dishDTO
* @return
*/
@PutMapping
@ApiOperation("修改菜品")
public Result update(@RequestBody DishDTO dishDTO) {
log.info("修改菜品:{}", dishDTO);
dishService.updateWithFlavor(dishDTO);
//将所有的菜品缓存数据清理掉,所有以dish_开头的key
clearCache("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_开头的key
clearCache("dish_*");
return Result.success();
}
/**
* 清理缓存数据
* @param pattern
*/
private void clearCache(String pattern) {
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(keys);
}
}
缓存套餐
spring cache
一个框架,实现了基于 注解 的缓存功能
提供了一层抽象,底层可以切换不同的缓存实现:EHCache Caffeine Redis
//pom.xml文件导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
常见注解
@EnableCaching 开启缓存注解,常加在启动类上
@Cacheable 在方法执行前先查询缓存中是否有数据,有的话,返回缓存数据;没有的话,调用方法并将返回值放到缓存中
@CachePut 将方法的返回值放到缓存中
@CacheEvict 将一条或多条数据从缓存中删除
快速入门
//创建user数据库
//application配置数据库和redis链接 引入pom注解(redis和mybatis主要是)
server:
port: 8888
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring_cache_demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: root
redis:
host: localhost
port: 6379
password: 123456
database: 1
//启动类application中
@EnableCaching//开启缓存注解功能
public class CacheDemoApplication {}
//controller
@PostMapping
@CachePut(cacheNames = "userCache",key = "#user.id")//如果使用spring cache缓存数据,key的生成,userCache::id
public User save(@RequestBody User user){}
@Cacheable(cacheNames = "userCache",key = "#id")//到redis查是否有没有测调用方法并缓存到日redis
public User getById(Long id){
User user = userMapper.getById(id);
return user;
}
//清除多条redis中的缓存
@CacheEvict(cacheNames = "userCache",key = "#id") //删除某个key对应的缓存数据
public void deleteById(Long id){
userMapper.deleteById(id);
}
@CacheEvict(cacheNames = "userCache",allEntries = true) //删除userCache下所有的缓存数据
实现思路
导入spring cache和redis相关maven坐标
启动类加上@EnableCaching注解,开启缓存注解功能
用户端接口setmealcontroller的 list 方法加入 @Cacheable注解
管理端 setmealcontroller 的 save,delete,update,startorstop方法加入CacheEvict注解
代码开发
//pom.xml文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
//启动类加入EnableCaching
@EnableCaching//开启缓存注解功能
//查询分类
@Cacheable(cacheNames = "setmealCache",key = "#categoryId")
public Result<List<Setmeal>> list(Long categoryId) {}
//删除
@CacheEvict(cacheNames = "setmealCache",allEntries = true)
public Result delete(@RequestParam List<Long> ids) {}
@CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")
public Result save(@RequestBody SetmealDTO setmealDTO) {}
添加购物车
//controller
@RestController
@RequestMapping("/user/shoppingCart")
@Slf4j
@Api(tags = "C端-购物车接口")
public class ShoppingCartController {
@Autowired
private ShoppingCartService shoppingCartService;
/**
* 添加购物车
* @param shoppingCartDTO
* @return
*/
@PostMapping("/add")
@ApiOperation("添加购物车")
public Result<String> add(@RequestBody ShoppingCartDTO shoppingCartDTO) {
log.info("添加购物车:{}", shoppingCartDTO);
shoppingCartService.addShoppingCart(shoppingCartDTO);
return Result.success();
}
}
//service
@Service
public class ShoppingCartServiceImpl implements ShoppingCartService {
@Autowired
private ShoppingCartMapper shoppingCartMapper;
@Autowired
private DishMapper dishMapper;
@Autowired
private SetmealMapper setmealMapper;
/**
* 添加购物车
* @param shoppingCartDTO
*/
@Override
public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO, shoppingCart);
// 只能查询自己的购物车数据
shoppingCart.setUserId(BaseContext.getCurrentId());
// 判断当前商品是否在购物车中
List<ShoppingCart> shoppingCartsList = shoppingCartMapper.list(shoppingCart);
if (shoppingCartsList != null && shoppingCartsList.size() > 0) {
// 如果存在,就更新数量,+1
shoppingCart = shoppingCartsList.get(0);
shoppingCart.setNumber(shoppingCart.getNumber() + 1);
shoppingCartMapper.updateNumberById(shoppingCart);
} else {
// 如果不存在,插入数据
Long dishId = shoppingCartDTO.getDishId();
if (dishId != null) {
// 添加到购物车的是菜品
Dish dish = dishMapper.getById(dishId);
shoppingCart.setName(dish.getName());
shoppingCart.setImage(dish.getImage());
shoppingCart.setAmount(dish.getPrice());
} else {
// 添加到购物车的是套餐
Setmeal setmeal = setmealMapper.getById(shoppingCartDTO.getSetmealId());
shoppingCart.setName(setmeal.getName());
shoppingCart.setImage(setmeal.getImage());
shoppingCart.setAmount(setmeal.getPrice());
}
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
shoppingCartMapper.insert(shoppingCart);
}
}
}
//mapper 接口
@Mapper
public interface ShoppingCartMapper {
/**
* 条件查询
* @param shoppingCart
* @return
*/
List<ShoppingCart> list(ShoppingCart shoppingCart);
//resource下mapper.xml文件
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into shopping_cart (name, image, user_id, dish_id, setmeal_id, dish_flavor, amount, create_time)
values (#{name}, #{image}, #{userId}, #{dishId}, #{setmealId}, #{dishFlavor}, #{amount}, #{createTime});
</insert>
<select id="list" resultType="com.sky.entity.ShoppingCart">
select *
from shopping_cart
<where>
<if test="userId!=null">and user_id=#{userId}</if>
<if test="dishId!=null">and dish_id=#{dishId}</if>
<if test="setmealId!=null">and setmeal_id=#{setmealId}</if>
<if test="dishFlavor!=null">and dish_flavor=#{dishFlavor}</if>
</where>
order by create_time desc
</select>
查看购物车
//controller
@GetMapping("/list")
@ApiOperation("查看购物车")
public Result<List<ShoppingCart>> list(){
return Result.success(shoppingCartService.showShoppingCart());
}
//service
@Override
public List<ShoppingCart> showShoppingCart() {
return shoppingCartMapper.list(ShoppingCart.builder().userId(BaseContext.getCurrentId()).build());
}
清空购物车
//controller
@DeleteMapping("/clean")
@ApiOperation("清空购物车商品")
public Result<String> clean(){
shoppingCartService.cleanShoppingCart();
return Result.success();
}
//service
@Override
public void cleanShoppingCart() {
shoppingCartMapper.deleteByUserId(BaseContext.getCurrentId());
}