今天学习了如何添加缓存以及清理缓存(两种方式),完成了购物车的接口实现
1.完成了缓存菜品的接口(用java调用Redis方法实现)
2.完成了缓存套餐的接口(用注解的方法实现)
3.完成了添加,查询,删除,清空购物车的接口
1.思考:
许多用户同时访问外卖平台(用户端),那么我们请求查询数据的速度会降低(用户查询一个菜品,等了几秒钟才显现出来,体验感极差),那么我们可以通过什么方式来增加访问速率呢 ==》 Redis缓存(由于Redis数据是存入内存中的,而服务端访问内存的速率是快于访问硬盘的速率的(Mysql数据默认存于硬盘)) ==》 但是不能直接把大量数据都存入内存吧(内存不适合存储大量数据)
2.分析:由于我们用户端是对于菜品有菜品分类的,那么我们是不是可以根据分类id来存储具体数据
3.实现缓存菜品:
用户端点击查询菜品 ==》后端接收到请求 ==》判断当前分类下菜品缓存是否存在 ==》存在直接读缓存,返回缓存数据给用户端,不存在查询Mysql数据库,再进行Redis缓存数据,然后返回数据给用户端
4.清理缓存
分析:数据变更,那么我们缓存数据是不是也要改变,清理缓存,用户查询时再重新存入缓存,(新增,修改,批量删除,起售停售状态,当然这都是管理端接口的修改)
实现:
1.新增:前端传过来是一个菜品的基本数据(包含分类菜品id),那么我们可以直接通过分类菜品id删除Redis对应数据,等用户使用查询时,再存入Redis 2.修改:因为我们不知道管理员是否修改了分类id(要判断,需要多次查询数据库,过于繁琐),那么我们可以直接删除Redis所有数据,等用户使用查询时,再存入Redis 3.批量删除:由于是批量删除,可能会存在不同分类下的菜品删除,与上面修改菜品一样(需要判断,过于繁琐),那么我们可以直接删除Redis所有数据,等用户使用查询时,再存入Redis 4.起售停售状态:同样如此,传过来的参数是菜品id,你都不知道分类id,还需要查询数据库,那么我们可以直接删除Redis所有数据,等用户使用查询时,再存入Redis
注意:由于我们新增菜品默认菜品是停售状态,那么我们真正要展示给用户端还需要修改菜品状态(删除所有缓存),那么我们的新增的实现其实可以省略(你最终菜品还是需要修改状态(删除所有缓存)才能展示给用户)
1.简单方法:基于注解直接实现缓存操作(Spring Cache)
2.如何实现
1.导入Spring Cache坐标(POM中)
2.导入你想要的缓存实现坐标(EHCache ,Caffeine,Redis)
3.在方法中加入注解(具体功能与细节在知识点扩展中)
1.分析:由于我们既有套餐也有菜品(类型不同,而且当菜品类型相同时其口味也会有不同),那么我们前端约束每次用点击加号(添加一个),前端就发出一个请求,后端接收请求实现操作
2.表的设计
展示图
一般要将菜品(套餐)名称,价格,口味(套餐的口味是固定的),数量,图片展示出来 而购物车本质是存的菜品id,套餐id,口味,创建时间(而菜品,套餐的具体数据是不是要通过它们的id来查询) 那么我们为了节省查询时间,就给购物车这个表添加了要展示的数据字段(避免每次都重复查询数据库),这些字段(本身是不需要添加的)称为冗余字段(一般这些数据字段是稳定的,不轻易修改的)
3.具体实现
前端发送请求(就是用户点击加号) ==》后端接收参数(菜品id和口味数据/套餐id) ==》创建购物车实体类对象(赋值) ==》动态根据userId(用户id,唯一标识,一个用户对应一个购物车),dishId(菜品id),setmealId(套餐id),口味数据(因为用户点菜品可以点不同口味,我们需要根据口味查询菜品的具体数据),来查询数据库并且返回 ==》判断返回数据是否存在 ==》存在,在返回数据的基础上将数量加一,不存在则进行下列操作 ==》判断添加数据是菜品还是套餐 ==》具体类型赋对应值
注意:我们一定要设置用户id(而这个,是由于在拦截器(用户)代码中设置了线程空间(里面存入了当时登录用户的id)),我们要使用时就直接从线程中取出即可,每个用户的id对应不同的购物车
1.介绍:实现缓存存储
2.实现:
必写属性:cacheNames="名称" ==》指定缓存名称 key="Spring表达式语言" ==》下面
key的写法:
1.#方法形参名(如#user) 2.#result(方法返回值) 3.#p0(方法的第一位形参,依次类推#p1第二位形参) 4#a0(与3相同,就名称变了) 5.#root.args[](数组里填0,就代表方法第一位形参) 6.点 . 代表对象导航(就是取出对象里面的具体属性,如#user.name) 7.其实上面的最终作用就是生成一个key(只不过你可以利用不同的数据)
3.疑问:有什么用啊
1.因为用注解修饰,它是自动帮你存入缓存数据库中(Redis),那么你是不是需要指定一个key给它,值给你存 2.而且key是不是要不重复,而且key要定义的有意义,规律 3.而上面的两个属性就是来实现key的名称的 4.cacheNames代表的是这个类型数据的前缀名称(保持相同即可) 5. key则是你需要特意设置的不同值 6.最终组成效果:如cacheNames="userCache" ,key="#user.id" =》userCache::id(具体值)
注意:该注解是先执行完方法后才会执行注解的缓存功能(因此才能使用返回值数据)(一般#参数名称,使用的最多,其他的你可以自己翻看源码里面全部都有解释)
1.介绍:执行方法之前会拿cacheNames+key属性组成的key去Redis中查询,能查到数据直接返回该数据,不执行方法,不能查到数据,则是执行方法,再将方法的返回值存入Redis中
2.该注解的属性与@CachePut属性基本一致(名称一样,唯一不同就是key没有#result这个写法了,从介绍知)
3.注意:该注解优先方法执行(先查Redis,没有执行方法,将方法返回值存入Redis)
1.介绍:方法执行完成后,会根据cacheNames+key属性组成的key去Redis中删除单一数据,如果你想要删除所有数据,那么将key换成allEntries(设置该属性为true,则是删除所有数据,false则不删数据)
2.该注解的属性用法与@Cacheable基本一致
3.注意:该注解功能是方法执行完成之后才进行删除操作