当前位置: 首页 > news >正文

Java项目-苍穹外卖-Day07-redis缓存应用-SpringCache/购物车功能

文章目录

  • 前言
  • 缓存菜品
    • 问题分析和实现思路
    • 缓存菜品数据
    • 清理缓存数据
    • 功能测试
  • SpringCache
    • 介绍
    • 入门案例
  • 缓存套餐
  • 购物车功能
    • 添加购物车
      • 需求分析和产品原型
      • 测试
    • 查看购物车
    • 清空购物车

前言

本章节主要是进行用户端的购物车功能开发
和redis作为mysql缓存的应用以及SpringCache的介绍
因为很多人查询数据库会导致mysql的查询效率降低,可以通过redis作为缓存来解决

实现产品原型
在这里插入图片描述
基本可以看出一些功能
添加购物车
查看购物车
清空购物车
以及我们进行redis应用的缓存菜品和套餐
还有一个自己的作业 就是增减购物车内商品的功能

缓存菜品

问题分析和实现思路

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

缓存菜品数据

这个挺简单的
就是根据redis中有无对应数据来进行操作
注意redis返回的数据类型(存入时是什么,取出来就是什么)
redis中的string可以对应java中的任意类型

@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_分类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);}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);}}

清理缓存数据

有可能我们管理端会修改/删除/新增菜品
你们上一次的查询到redis的要跟着更新
要不然sql和redis中数据不一致,管理端作出的更改用户端如果还是去查redis就看不到
这里就只写
admin中DishController中修改的方法

  @PostMapping@ApiOperation("新增菜品")public Result save(@RequestBody DishDTO dishDTO){log.info("新增菜品:{}",dishDTO);dishService.saveWithFlavor(dishDTO);//清理缓存数据cleanCache("dish_"+dishDTO.getCategoryId());return Result.success();}@DeleteMapping@ApiOperation("菜品的批量删除")public Result delete(@RequestParam List<Long> ids){//传参为1,2,3这种,想要mvc帮我们自动封装需要用到@RequestParam,否则只能字符串接收自己解析log.info("菜品批量删除:{}",ids);dishService.deleteBatch(ids);//这个比较复杂,所以直接全部删除 dish_*就表示以dish_开头的key//需要先将对应全部key取出然后再删除cleanCache("dish_*");return Result.success();}@PutMapping@ApiOperation("修改菜品")public Result update(@RequestBody DishDTO dishDTO) {log.info("修改菜品,对应信息为:{}",dishDTO);dishService.updateWithFlavor(dishDTO);//仔细想想更改 可以设计一个分类(只更改名称价格什么的) 也可以涉及两个(菜品分类的变更)//所以这里也清理掉所有缓存cleanCache("dish_*");return Result.success();}@PostMapping("/status/{status}")@ApiOperation("起售,停售菜品")public Result startOrStop(@PathVariable Integer status,Long id){log.info("起售停售菜品:{},{}",status,id);dishService.startOrStop(status,id);//这里也全部清理掉,需要查表对应分类id情况有点复杂cleanCache("dish_*");return Result.success();}这个是新增方法/*** 清理缓存数据* @param pattern*/private void cleanCache(String pattern){Set keys = redisTemplate.keys(pattern);redisTemplate.delete(keys);}

功能测试


看对应更换分类后有没有sql语句输出
和修改分类菜品后对应的分类菜品情况

SpringCache

介绍

在这里插入图片描述
在这里插入图片描述
admin,增删改数据只需要清除缓存,CachePut是user那边查询数据时同步缓存用的
只有需要查才会放入缓存
@Cacheable用于select(查询)
@CachePut一般用于新增
@CacheEvict就是用户端的更改,删除等等

入门案例

导入依赖
redis和springcache
其他正常的那些依赖就不介绍了
在这里插入图片描述
再进行一下文件配置
在这里插入图片描述
controller里面提起写好了方法主要是学一下springcache
在这里插入图片描述
往下就是使用springcache的正常流程了
1.再springboot启动类开启缓存注解
在这里插入图片描述

比如这个请求
我们存储用户,一般是希望同时存储到缓存中
所以我们用==@CachePut修饰==
对应的属性 cacheNames和key是与redis中的key有关的
redis中的key=cacheNames::key
key一般是动态获取,使不同用户对应不同key,可以获取user的id
格式是 key = #user(参数).id
有人可能问刚开始还没传id,怎么获取对应id,user参数和user返回值是一个,然后其实操作是先插入到sql数据库然后再进行缓存的(mybatis进行操作返回id给user对象)
在这里插入图片描述
也可以这样写,result引用方法的返回值
在这里插入图片描述
其实还有很多用法
但是还是最推荐第一种

查询相关的语句,先看redis有没有,没有查数据库的然后存到redis中
用到@Cacheable注解,这里就不能用result,具体可以看注解里有对应的注释说明
然后我们要查询的key就是userCache::id (因为我们新增就是用这个,然后查询也要用这个可以)

在这里插入图片描述
删除一条数据
使用@CaheEvict来进行缓存数据的删除,保障数据库和缓存的数据一致性

在这里插入图片描述
删除所有的缓存的键值对
把key属性变为allEntries属性并且设为true
在这里插入图片描述

缓存套餐

在这里插入图片描述
在这里插入图片描述
改的不多
这是admin包下的setmealController

@RestController
@RequestMapping("/admin/setmeal")
@Api("套餐相关接口")
@Slf4j
public class SetmealController {/*** 新增套餐*/@Autowired SetmealService setmealService;@PostMapping@ApiOperation("新增套餐")@CacheEvict(cacheNames = "setmealCache",key = "#setmealDTO.categoryId")public Result save(@RequestBody SetmealDTO setmealDTO){log.info("新增套餐信息:{}",setmealDTO);setmealService.saveWithDish(setmealDTO);return Result.success();}/*** 根据id查询套餐* @param id* @return*/@GetMapping("/{id}")@ApiOperation("根据id查询套餐")public Result<SetmealVO> getById(@PathVariable Long id){SetmealVO setmealVO = setmealService.getByIdWithDish(id);return Result.success(setmealVO);}/*** 分页查询* @param setmealPageQueryDTO* @return*/@GetMapping("/page")@ApiOperation("分页查询")public Result<PageResult> page(SetmealPageQueryDTO setmealPageQueryDTO){log.info("套餐分页查询:{}",setmealPageQueryDTO);PageResult pageResult = setmealService.pageQuery(setmealPageQueryDTO);return Result.success(pageResult);}/*** 批量删除套餐* @param ids* @return*/@DeleteMapping@ApiOperation("批量删除套餐")@CacheEvict(cacheNames = "setmealCache",allEntries = true)public Result delete(@RequestParam List<Long> ids){//让Spring自动处理字符串变成集合log.info("批量删除套餐:{}",ids);setmealService.deleteBatch(ids);return Result.success();}/*** 修改套餐* @param setmealDTO* @return*/@PutMapping@ApiOperation("修改套餐")@CacheEvict(cacheNames = "setmealCache",allEntries = true)public Result update(@RequestBody SetmealDTO setmealDTO){log.info("修改套餐信息:{}",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();}}

购物车功能

添加购物车

需求分析和产品原型

菜品:设置口味数据:点击的是选择规格然后才能加入购物车
未设置口味数据:直接加入
套餐:直接加入
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
设置冗余字段可以增加查询速度(不用查多表查单表即可)
在这里插入图片描述
注意点非常多,代码还是挺难实现的,建议仔细看看不同情况 菜品 套餐的处理
菜品有无口味,如果包含该菜品/套餐需要实现数量+1而不是新增操作了
设计多个表 setmeal dish 等 且可能涉及回查操作
重点就是搞清我们插入需要什么数据,什么数据还没有,怎么进行获取!!!

ShoppingCartController

@RestController
@RequestMapping("/user/shoppingCart")
@Slf4j
@Api(tags = "C端购物车相关接口")
public class ShoppingCartController {@Autowiredprivate ShoppingCartService shoppingCartService;/*** 添加购物车* @param shoppingCartDTO* @return*/@PostMapping("/add")@ApiOperation("添加购物车")public Result add(@RequestBody ShoppingCartDTO shoppingCartDTO){log.info("添加购物车,商品信息为:{}",shoppingCartDTO);shoppingCartService.addShoppingCart(shoppingCartDTO);return Result.success();}
}

ShoppingCartMapper

@Mapper
public interface ShoppingCartMapper {/*** 动态条件查询* @param shoppingCart* @return*/List<ShoppingCart> list(ShoppingCart shoppingCart);/*** 根据id修改商品数量* @param shoppingCart*/@Update("update shopping_cart set number = #{number} where id= #{id}")void updateNumberById(ShoppingCart shoppingCart);/**** @param shoppingCart*/@Insert("insert into shopping_cart(name,user_id,dish_id,setmeal_id,dish_flavor,number,amount,image,create_time)" +"values(#{name},#{userId},#{dishId},#{setmealId},#{dishFlavor},#{number},#{amount},#{image},#{createTime}) ")void insert(ShoppingCart shoppingCart);
}

对应xml文件

<mapper namespace="com.sky.mapper.ShoppingCartMapper"><select id="list" resultType="com.sky.entity.ShoppingCart">select * from shopping_cart<where><if test="userId != null">and user_id = #{userId}</if><if test="setmealId != null">and setmeal_id = #{setmealId}</if><if test="dishId != null">and dish_id = #{dishId}</if><if test="dishFlavor != null">and dish_flavor = #{dishFlavor}</if></where></select>
</mapper>

重量级ShoppingCartServiceImpl

@Service
@Slf4j
public class ShoppingCartServiceImpl implements ShoppingCartService {@Autowiredprivate ShoppingCartMapper shoppingCartMapper;@Autowiredprivate DishMapper dishMapper;@Autowiredprivate SetmealMapper setmealMapper;/*** 添加购物车* @param shoppingCartDTO*/public void addShoppingCart(ShoppingCartDTO shoppingCartDTO) {//添加购物车两种情况1.本身就在购物车里面 就让对应的number+1 不是的话就添加//判断是否已经存在ShoppingCart shoppingCart = new ShoppingCart();BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);//还少userIdLong currentId = BaseContext.getCurrentId();shoppingCart.setUserId(currentId);List<ShoppingCart> list = shoppingCartMapper.list(shoppingCart);//如果存在,只需要将数量+1//实际上只能查出来一条if (list != null && list.size()>0){ShoppingCart cart = list.get(0);cart.setNumber(cart.getNumber()+1);//update shopping_cart set number = ? where id = ?shoppingCartMapper.updateNumberById(cart);}else {//如果不存在,只需要插入一条购物车数据//然后是插入,但是插入的话前段没提供name,image等信息所以要先查一下//菜品差菜品表,套餐查套餐表//判断是菜品还是套餐Long dishId = shoppingCartDTO.getDishId();if(dishId != null){//菜品Dish dish = dishMapper.getById(dishId);shoppingCart.setName(dish.getName());shoppingCart.setImage(dish.getImage());shoppingCart.setAmount(dish.getPrice());//购物车价格是amount}else {Long setmealId = shoppingCartDTO.getSetmealId();Setmeal setmeal = setmealMapper.getById(setmealId);shoppingCart.setName(setmeal.getName());shoppingCart.setImage(setmeal.getImage());shoppingCart.setAmount(setmeal.getPrice());//购物车价格是amount}shoppingCart.setNumber(1);//第一次添加数量肯定为1shoppingCart.setCreateTime(LocalDateTime.now());shoppingCartMapper.insert(shoppingCart);}}
}

测试


自己去把套餐和对应的菜品都添加一下,再看看数量变化,一个菜品+两次debug看一下对应语句对不对
再去数据库检查一下

查看购物车

产品原型
在这里插入图片描述
接口设计
在这里插入图片描述
controller

    /*** 查看购物车* @return*/@GetMapping("/list")@ApiOperation("查看购物车")public Result<List<ShoppingCart>> list(){List<ShoppingCart> list = shoppingCartService.showShoppingCart();return Result.success(list);}
}

ShoppingCartServiceImpl

/*** 查看购物车* @return*/public List<ShoppingCart> showShoppingCart() {Long id = BaseContext.getCurrentId();List<ShoppingCart> list = shoppingCartMapper.list(ShoppingCart.builder().userId(id).build());return list;}
}

mapper的list是用之前的定义的方法所以没有mapper层
自己测试

清空购物车

在这里插入图片描述
controller

    /*** 清空购物车* @return*/@ApiOperation("清空购物车")@DeleteMapping("/clean")public Result clean(){shoppingCartService.cleanShoppingCart();return Result.success();}
}

service

    /*** 清空购物车数据*/public void cleanShoppingCart() {Long id = BaseContext.getCurrentId();shoppingCartMapper.deleteByUserId(id);}
}

Mapper

    /*** 清空购物车* @param id*/@Delete("delete  from shopping_cart where user_id=-#{id}")void deleteByUserId(Long id);
}

自己测试

http://www.lryc.cn/news/149426.html

相关文章:

  • 零知识证明(zk-SNARK)(一)
  • linux中打印数据的行缓冲模式
  • 香橙派OrangePi zero H2+ 驱动移远4G/5G模块
  • 自动驾驶——【规划】记忆泊车特殊学习路径拟合
  • 【跟小嘉学 Rust 编程】十六、无畏并发(Fearless Concurrency)
  • Android 进阶——图形显示系统之VSync和 Choreographer的创建详解(一)
  • SQL Server开启变更数据捕获(CDC)
  • 八、性能测试
  • 景芯SoC 芯片全流程培训
  • 目标检测后的图像上绘制边界框和标签
  • Leetcode: 1. 两数之和 【题解超详细】
  • PHP 通过 Redis 解决并发请求的操作问题
  • 浅谈信息论和信息编码
  • 【测试】笔试02
  • 公司内部网段多管控乱,该如何规范跨网文件传输交换?
  • Ceph入门到精通-OSD waring 设置建议
  • 软件测试工程师如何快速理解业务?
  • 【教程】部署apprtc服务中安装google-cloud-cli组件的问题及解决
  • C++——shared_ptr:make_shared的用处,与shared_ptr直接构造的区别
  • 【网络安全带你练爬虫-100练】第17练:分割字符串
  • Unity 之ToolTip的用法
  • xsschallenge通关(11-15)
  • Kubernetes技术--k8s核心技术集群的安全机制RBAC
  • 【JavaSE】String类
  • DBMS Scheduler设置重复间隔
  • windows的redis配置sentinel
  • NetMarvel机器学习促广告收益最大化,加速获客
  • Spring-5.0.x源码下载及本地环境搭建
  • go中的切片
  • C++笔记之单例通过GetInstance传递参数