前言:
今天写第七天的内容,主要介绍一下spring-cache,还有自己的一些问题,思考,踩坑点
今日所学:
- 缓存菜品
- spring-cache介绍以及应用
- 增删购物车
目录
1.缓存菜品
问题:
如何缓存菜品至redis,以此降低对mysql的访问量
实现思路:
核心代码:
这里以user包下的DishController类的list方法为例,进行说明
第一步我们动态构造key(确保每个分类缓存的key不同),接着实现过程就跟实现思路那张图所描绘的一致了。我们先是查询redis是否有改类菜品数据,如果有,直接返回redis的菜品数据即可。如果没有,在从mysql进行查询,并把数据存入到redis中
2. spring-cache介绍以及应用
2.1 介绍
Spring Cache 是 Spring 框架提供的一个缓存抽象层,旨在简化在应用程序中使用缓存的过程。它通过注解的方式,允许开发者在不修改业务逻辑代码的情况下,轻松地添加缓存功能。Spring Cache 支持多种缓存实现,如 EhCache、Guava、Caffeine 等,并且可以与 Spring 的其他功能无缝集成。
上面阿巴阿巴说了这么多,其实我们不用关心。
我们只需要记住这玩意跟事务(@Transactional)类似,基于注解实现,也就是简单一个注解,就能实现缓存功能
下面介绍下spring-cache常用的几个方法
2.2 EnableCaching
作用:
启动类上开启缓存注解功能(别忘了!!)
2.3 Cacheput
作用:
将方法的返回值放到缓存中(方法的返回值画重点)
key键的生成规则:
如果使用spring_cache缓存数据,key键的生成:usercache::key(key由#加方法的参数名决定)
2.4 Cacheable
作用:
在方法执行前查询缓存中是否有数据,如果有数据,则直接返回缓存数据,如果没有缓存数据,调用方法并将方法返回值放到缓存中(就我们上面缓存菜品举例的那个功能)
key键的查询规则:
查询是否存在usercache::id的key,存在直接返回数据(不用执行方法),不存在在调用方法
这里cacheable和cacheput的cacheNames最好保持一致(要确保生成的key和查询的key是一致的)
底层:
使用代理技术(类似于AOP),先进行缓存的查询,再执行getById方法
就是我们上面举例的那个缓存菜品功能封装成一个注解了(可以这样理解,真实执行getById方法用的是反射)
2.5 CacheEvict
作用:
将一条或多条数据从缓存中删除
下面是清理一条缓存数据
也很容易看懂,精确key键就行了
再下面这个是清理所有缓存数据的
思考:
如果是清理多条数据呢
感兴趣的可以问下你的deepseek,这里不展开
2.6 spring-cache应用
具体操作是在admin和user端的setmealController
这里我着重讲下为什么admin端新增套餐要删除原有缓存数据
有人肯定不理解,新增套餐不应该是添加缓存数据,每个套餐生成的key值都是不同的,不会覆盖原有key值,为什么不用@Cacheput。这里我建议在仔细看下@Cacheput的功能:
将方法的返回值放到缓存中
这个方法的返回值是什么呢?是Result(空空如也的那种)
知道这个后,我们再讲下为什么使用@CacheEvict
这里主要是为了及时更新同一类别下套餐的数量,我们知道套餐和类别是n : 1的关系(感兴趣的可以看下我上条博客,有表之间的E-R图),在user端当我们第一次根据类别id查询套餐的时候,已经把同一类别所有的套餐都加入缓存了(@Cacheable的功能)
而回到admin端,我们在数据库里给当前类别新增了一个套餐后,并没有同步到cache里,
导致users端一直是查询cache里的旧数据(也是@Cacheable的功能),所以加入新套餐后,要清空当前类别的缓存,让user端查找时重新加入
3.增删购物车
3.1 增加购物车
问题:
如何添加购物车商品
解决思路:
跟其他添加操作的功能接口一样,其实也就是数据从controller层传入,经过service层的逻辑处理,传到mapper层储存到相应的数据库中(这里是shopping_cart库)
但是这个功能接口的难点在于:
- 要判断是否这次添加的是否是相同的菜品或者是套餐,是的话Amount+1即可
- 要判断添加的是菜品还是套餐,因为他们两添加的字段并不一样
确定了这两点,剩下的就是很平常的insert操作
核心代码
(这里仅展示service层的代码)
1.判断添加的是否是相同的菜品或者是套餐
这里注意userId由ThreadLocal获得(老朋友了,用处是动态获取id),Mapper.list映射的是select的动态SQL,查找是否有该商品,找到了,数量加一,再进行数据库更新即可
2.判断添加的是菜品还是套餐
判断方法很简单,获取shoppingCart.getDishId()或者是getsetmealId()的值,哪个不为空,就添加哪个
另外说一下,这里id是自增生成的,user_id是由ThreadLocal传入的,dish_id或setmeal_id跟dish_flavor(如果是传入的dish_id)都是前端传入的。所以我们只需要再设置name,image,amount,number,createTime的值就行了。
最后插入该数据
3.2 查看/清空购物车
非常简单的操作,这里仅说下注意点
注意点:
这里注意购物车的区分是由userId(查看同一用户的购物车,清空同一用户的购物车)确定的(不是由id),查询前要先插入userId的值,作为where查询的条件
下面的清空购物车也是按照也是依据userId来删除的
3.3 删除一条购物车(补充)
这个课程中没有涉及,但是接口文档中有,就顺带给做了,感兴趣的可以看一看
解决思路:
逻辑跟添加购物车是一样的(甚至简单点),先判断是否存在这条数据,存在的话判断num数量是否是大于零,大于零的话数量减一(后面记得更新),当num==0时,删除这一条数据
这边注意下先判断大于零再number-1,这边设计表的时候number没有加约束,给我干道-30来了。
金额更是成了负数(用户狂喜)
3.4 思考
我们能否用redis来储存这些商城数据