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.rhkb.cn/news/115372.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python基础知识学习与回顾

Python学习 Python基本语法 标识符 标识符由数字、字符串、下划线构成。 注意事项&#xff1a; 标识符不以数字开头区分大小写下划线开头的标识符具有特殊意义保留字&#xff0c;Python保留了一些关键字&#xff0c;这些关键字都是通过小写字母进行保存。 下划线开头的特…

在k8s中用label控制Pod部署到指定的node上

案例-标注k8s-node1是配置了SSD的节点 kubectl label node k8s-node1 disktypessd 查看标记 测试 将pod部署到disktypessd的节点上&#xff08;这里设置了k8s-node1为ssd&#xff09; 部署后查看结果-副本全都运行在了k8s-node1上—符合预期 删除标记 kubectl label node k8…

Camera | 12.瑞芯微摄像头自动焦距马达驱动移植

本为你主要讲解如何让摄像头ov13850支持自动对焦功能。 摄像头的对角主要通过VCM马达驱动芯片DW9714来实现的。 一、环境 soc : rk3568 board: EVB1-DDR4-V10 软 件&#xff1a;Android 11 Linux&#xff1a;4.19.232 Camera:ov13850二、DW9714 1.DW9714简介 DW9714专…

【已解决+吐槽】pip install cn2an报错 Cannot uninstall ‘ruamel_yaml‘

我需要用cn2an模块将中文的数字转化为阿拉伯数字&#xff0c;但在安装cn2an的过程中出现了以下报错&#xff1a; 于是乎&#xff0c;我跟着CSDN上诸如此类的教程开始跟nodejs死磕&#xff0c;折腾了大半天&#xff0c;以下是各种尝试。这不是重点&#xff0c;我主要是吐槽&…

中文完形填空

本文通过ChnSentiCorp数据集介绍了完型填空任务过程&#xff0c;主要使用预训练语言模型bert-base-chinese直接在测试集上进行测试&#xff0c;也简要介绍了模型训练流程&#xff0c;不过最后没有保存训练好的模型。 一.完形填空 完形填空应该大家都比较熟悉&#xff0c;就是把…

Spring Cloud Alibaba-Sentinel规则

1 流控规则 流量控制&#xff0c;其原理是监控应用流量的QPS(每秒查询率) 或并发线程数等指标&#xff0c;当达到指定的阈值时 对流量进行控制&#xff0c;以避免被瞬时的流量高峰冲垮&#xff0c;从而保障应用的高可用性。 第1步: 点击簇点链路&#xff0c;我们就可以看到访…

现代化畜牧业行业分析 - 商品猪养殖

改革开放以来&#xff0c;中国畜牧业生产基础条件不断改善、生产方式快速转变&#xff0c;畜牧业综合生产能力和保障市场有效供应能力不断加强。中国肉类、禽蛋产量均居世界第一位&#xff0c;奶类产量居世界第三位。随着产量的增长&#xff0c;中国人均畜产品占有量也持续上升…

联网智能实时监控静电离子风机的工作流程

联网智能实时监控静电离子风机是通过将静电离子风机与互联网连接&#xff0c;实现对其状态和性能的远程监控和管理。 具体实现该功能的方法可以包括以下几个步骤&#xff1a; 1. 传感器安装&#xff1a;在静电离子风机上安装适当的传感器&#xff0c;用于感知相关的参数&…

微信开发之一键创建标签的技术实现

简要描述&#xff1a; 添加标签 请求URL&#xff1a; http://域名地址/addContactLabel 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型说明…

Python面试:什么是GIL

1. GIL (Global Interpreter lock)可以避免多个线程同时执行字节码。 import threadinglock threading.Lock()n [0]def foo():with lock:n[0] n[0] 1n[0] n[0] 1threads [] for i in range(5000):t threading.Thread(targetfoo)threads.append(t)for t in threads:t.s…

python conda实践 sanic框架gitee webhook实践

import subprocess import hmac import hashlib import base64 from sanic.response import text from sanic import Blueprint from git import Repo# 路由蓝图 hook_blue Blueprint(hook_blue)hook_blue.route(/hook/kaifa, methods["POST"]) async def kaifa(req…

UI自动化之关键字驱动

关键字驱动框架&#xff1a;将每一条测试用例分成四个不同的部分 测试步骤&#xff08;Test Step&#xff09;&#xff1a;一个测试步骤的描述或者是测试对象的一个操作说明测试步骤中的对象&#xff08;Test Object&#xff09;&#xff1a;指页面的对象或者元素对象执行的动…

rtsp 拉流 gb28181 收流 经AI 算法 再生成 rtsp server (一)

1、 rtsp 工具 1 vlc 必备工具 2 wireshark 必备工具 3 自己制作的工具 player 使用tcp 拉流&#xff0c;不自己写的话&#xff0c;使用ffmpeg 去写一个播放器就行 4 live555 编译好live555&#xff0c; 将live555的参数修改以下&#xff0c;主要是缓存大小 文章使用c 来写一…

C++面试题(叁)---操作系统篇

目录 操作系统篇 1 Linux中查看进程运行状态的指令、查看内存使用情况的指令、 tar解压文件的参数。 2 文件权限怎么修改 3 说说常用的Linux命令 4 说说如何以root权限运行某个程序。 5 说说软链接和硬链接的区别。 6 说说静态库和动态库怎么制作及如何使用&#xff0c;区…

界面控件DevExpress WinForms(v23.2)下半年发展路线图

本文主要概述了官方在下半年&#xff08;v23.2&#xff09;中一些与DevExpress WinForms相关的开发计划&#xff0c;重点关注的领域将是可访问性支持和支持.NET 8。 DevExpress WinForms有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。同时能…

自然语言处理-NLP

目录 自然语言处理-NLP 致命密码&#xff1a;一场关于语言的较量 自然语言处理的发展历程 兴起时期 符号主义时期 连接主义时期 深度学习时期 自然语言处理技术面临的挑战 语言学角度 同义词问题 情感倾向问题 歧义性问题 对话/篇章等长文本处理问题 探索自然语言…

四、高并发内存池整体框架设计

四、高并发内存池整体框架设计 现代很多的开发环境都是多核多线程&#xff0c;在申请内存的场景下&#xff0c;必然存在激烈的锁竞争问题。malloc本身其实已经很优秀&#xff0c;那么我们项目的原型TCmalloc就是在多线程高并发的场景下更胜一筹&#xff0c;所以这次我们实现的…

centos 7的超详细安装教程

打开虚拟机&#xff0c;创建一个新电脑 我们选择经典&#xff0c;然后选择下一步 我们选择稍后安装&#xff0c;我们在后面进行改设备 因为centos系统是linux系统的一个版本&#xff0c;所有我们选择linux&#xff0c;版本选择centos 7 64位&#xff0c;然后就是点击下一步 这一…

HTML <template> 标签

实例 使用 <template> 保留页面加载时隐藏的内容。使用 JavaScript 来显示: <button οnclick="showContent()">显示被隐藏的内容</button><template><h2>Flower</h2><img src="img_white_flower.jpg" width=&q…

2023年03月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

第1题&#xff1a;拼点游戏 C和S两位同学一起玩拼点游戏。有一堆白色卡牌和一堆蓝色卡牌&#xff0c;每张卡牌上写了一个整数点数。C随机抽取n张白色卡牌&#xff0c;S随机抽取n张蓝色卡牌&#xff0c;他们进行n回合拼点&#xff0c;每次两人各出一张卡牌&#xff0c;点数大者获…