文章
苍穹外卖day07
一:导入商品浏览接口
跟着视频在资料包里导入就行了。和之前一样都是crud
二:缓存菜品
问题说明,我们将数据存放在数据库,当有大量的用户访问数据库时,数据库的性能会下降,压力提高;我们可以使用缓存,减少数据库访问的压力
实现思路,用户查询菜品,我们先看缓存中有没有菜品,如果没有就查询数据库,然后再存入缓存
将菜品保存在缓存中,我们在controller中找到查询所有菜品的方法,然后做判断,先通过key来获取缓存的value,如果缓存中存在这个key就获取,然后直接返回,如果不存在就调用方法查询数据库,获得的菜品再存入缓存:
java
@GetMapping("/list")
@ApiOperation("根据分类id查询菜品")
public Result<List<DishVO>> list(Long categoryId) {
String key ="dish_"+categoryId;
ValueOperations valueOperations = redisTemplate.opsForValue();
if (redisTemplate.hasKey(key)){
List<DishVO> res = (List<DishVO>) valueOperations.get(key);
return Result.success(res);
}
Dish dish = new Dish();
dish.setCategoryId(categoryId);
dish.setStatus(StatusConstant.ENABLE);//查询起售中的菜品
List<DishVO> list = dishService.listWithFlavor(dish);
valueOperations.set(key,list);
return Result.success(list);
}
我们统一将redis中存储分类菜品数据的key变成"dish_"+categoryId,然后需要注意的是,我们从redis中获取到时候key必须的字符穿类型的,value可以是任意类型的,我们取得时候就按存入时的类型去强制类型转换就行;
然后我们要进行菜品信息修改的话,小程序还是会从缓存中取数据而我们数据库的信息已经修改了,这个时候就要进行清理缓存的操作了,我们要思考一下,进行添加菜品需要清理吗,需要;因为我们菜品分类下多了一个菜品,修改菜品也需要,而且如果是修改分类的话,那么就会影响到两个缓存数据,为了方便呢,我们直接清理全部缓存即可,然后批量删除菜品也是,可能同时影响多个分类,所以我们也要清理全部缓存;
我们再admin下的菜品管理接口下进行清理即可:
java
private void clearCache(String pattern){
Set keys = redisTemplate.keys(pattern);
redisTemplate.delete(pattern);
}
定义一个方法,代表按照pattern的格式去清理缓存;
修改,删除操作:
java
clearCache("dish_*");
清楚所有;
添加其实没必要进行清理,因为我们默认添加的菜品是禁售的;
三:springcache
1:enablecaching:开启缓存注解功能
2:cacheable:放在方法上看缓存中有没有数据,有直接返回,没有将方法的返回值放入缓存
3:cacheput:将方法的返回值放到缓存中;
4:cacheevict:清理缓存
具体使用:
java
@Cacheable(cacheNames = "userCache",key = "#id" )
1:cacheable:这里放在方法上:通过userCache和key来拼接redis中的key,然后他会直接取redis中取查有没有存在我们拼接的这个key,如果有就直接返回,不调用方法,如果没有就通过反射来调用方法,然后将方法的返回值直接添加到我们拼接的key中;
java
@CachePut(cacheNames = "userCache" ,key = "#user.id")
2:CachePut是直接将方法的返回值插入到redis中,然后前缀都是一样的,后面的key想要不一样就要动态设置,#后可以调用我们的参数,#result可以调用我们方法的返回值,然后#p可以知道是我们第几个参数,而且这个插入是当方法执行完毕后才会插入的;
java
@CacheEvict(cacheNames = "userCache",key = "#id")
java
@CacheEvict(cacheNames = "userCache",allEntries = true)
3:cacheEvict:这个就是清理缓存数据,我们可以一次清理一个和上面的命名方法一样,也可以使用allEntries将userCache下的全部数据清除;
四:缓存套餐
其实很简单只要再用户端查询套餐的接口上加上cacheable注解,这样,当我们的redis中没有时我们会执行方法,然后将返回值插入到redis,插入的时result对象,当然我们取出的也是result,前端需要的也是result,当我们redis中有key时就直接返回
java
@Cacheable(cacheNames = "setmeal",key = "#categoryId")
清理缓存:当套餐增加了,对应的分类就要清除缓存,不然查询不到我们增加的套餐,当套餐修改了,如果修改的是套餐种类,那么可能会影响两个套餐种类,所有要清楚所有缓存,如果套餐批量删除,也可能会影响多个分类,所以,也要清楚全部缓存
java
@CacheEvict(cacheNames = "setmeal",key = "#setmealDTO.categoryId")
java
@CacheEvict(cacheNames = "setmeal",allEntries = true)
五:添加购物车
添加到购物车的service要麻烦一些:
controller:
java
@PostMapping("/add")
public Result addCart(@RequestBody ShoppingCartDTO shoppingCartDTO){
log.info("添加购物车{}",shoppingCartDTO);
shopCartService.addCart(shoppingCartDTO);
return Result.success();
}
我们接收的是一个dto对象,dto中有套餐id和菜品id,但是只会有一个,要么是套餐,要么是菜品;
service:
java
@Override
public void addCart(ShoppingCartDTO shoppingCartDTO) {
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
shoppingCart.setUserId(BaseContext.getCurrentId());
List<ShoppingCart> shoppingCarts= shopCartMapper.list(shoppingCart);
//判断购物车是否已经存在菜品或者套餐
if (shoppingCarts.size()>0&&shoppingCarts!=null){
shopCartMapper.update(shoppingCart);
return;
}
//将购物车中的信息插入到表中
if (shoppingCartDTO.getDishId()!=null){//如果是菜品
DishVO dishVO = dishMapper.selectById(shoppingCartDTO.getDishId());
shoppingCart.setImage(dishVO.getImage());
shoppingCart.setAmount(dishVO.getPrice());
shoppingCart.setName(dishVO.getName());
shoppingCart.setDishId(shoppingCartDTO.getDishId());
}else if (shoppingCartDTO.getSetmealId()!=null) {//如果是套餐
Setmeal setmeal = setmealMapper.selectById(shoppingCartDTO.getSetmealId());
shoppingCart.setImage(setmeal.getImage());
shoppingCart.setAmount(setmeal.getPrice());
shoppingCart.setName(setmeal.getName());
shoppingCart.setSetmealId(shoppingCartDTO.getSetmealId());
}
shoppingCart.setCreateTime(LocalDateTime.now());
shopCartMapper.addCart(shoppingCart);
}
这边的业务逻辑比较复杂,我们要先看购物车里有没有当前传入的商品,如果有我们就只需要number+1就行了,然后我们先进行查询,看有没有我们当前传入的商品,我们根据dto中的菜品id或者套餐id,前边说过只会有一个所以sql这么写:
java<select id="list" resultType="com.sky.entity.ShoppingCart"> select * from sky_take_out.shopping_cart <where> <if test="dishId!=null"> dish_id=#{dishId} </if> <if test="setmealId!=null"> setmeal_id=#{setmealId} </if> <if test="dishFlavor!=null"> and dish_flavor=#{dishFlavor} </if> and user_id=#{userId} </where> </select>
这里面的useid是我们通过treadlocal获取的;返回的虽然是一个集合,但是里面要么只有一个元素,要么就是没有元素,因为一个用户的一个菜品只会有一条数据,然后我们判断是否为空,如果不为空说明购物车中有该物品,我们只需要将number加一就行了,所以这边是update语句:
java<update id="update"> update sky_take_out.shopping_cart set number= number+1 where <if test="dishId!=null"> dish_id=#{dishId} </if> <if test="setmealId!=null"> setmeal_id=#{setmealId} </if> and user_id=#{userId} </update>
可以看到我们通过用户id和商品id唯一的确定了一条数据,然后直接返回就行;
那么如果为空我们就要插入,这里可能是菜品,也可能是套餐,为方便后面调用相关的方法,我们分两种情况进行赋值,要赋值的有:商品名称,商品价格,商品的图片,商品的创建时间;
最后执行插入操作语句:
java@Insert("insert into sky_take_out.shopping_cart (name, image, user_id, dish_id, setmeal_id, dish_flavor, amount, create_time) " + "VALUES (#{name}, #{image}, #{userId}, #{dishId}, #{setmealId}, #{dishFlavor}, #{amount}, #{createTime})") void addCart(ShoppingCart shoppingCart);
就完成了!
mapper:见上;
六:查看购物车
controller:
java
@GetMapping("/list")
public Result select(){
List<ShoppingCart>shoppingCarts= shopCartService.select();
return Result.success(shoppingCarts);
}
service:
java
public List<ShoppingCart> select() {
ShoppingCart shoppingCart = new ShoppingCart();
shoppingCart.setUserId(BaseContext.getCurrentId());
List<ShoppingCart> list = shopCartMapper.list(shoppingCart);
return list;
}
直接调用我们之前已经定义的list方法就行;
mapper:
java
<select id="list" resultType="com.sky.entity.ShoppingCart">
select * from sky_take_out.shopping_cart
<where>
<if test="dishId!=null">
dish_id=#{dishId}
</if>
<if test="setmealId!=null">
setmeal_id=#{setmealId}
</if>
<if test="dishFlavor!=null">
and dish_flavor=#{dishFlavor}
</if>
and user_id=#{userId}
</where>
</select>
七:清空购物车
controller:
java
@DeleteMapping("/clean")
public Result clean(){
shopCartService.clean();
return Result.success();
}
service:
java
@Override
public void clean() {
shopCartMapper.clean(BaseContext.getCurrentId());
}
mapper:
java
@Delete("delete from sky_take_out.shopping_cart where user_id=#{currentId}")
void clean(Long currentId);
八:删除一个商品
这里其实是商品的数量-1,并且还有一个逻辑就是,当商品的数量为0时就删除这个商品
controller:
@PostMapping ("/sub")
public Result deleteOne(@RequestBody ShoppingCartDTO shoppingCartDTO){
shopCartService.updateOne(shoppingCartDTO);
return Result.success();
}
service:
java
@Override
public void updateOne(ShoppingCartDTO shoppingCartDTO) {
ShoppingCart shoppingCart = new ShoppingCart();
BeanUtils.copyProperties(shoppingCartDTO,shoppingCart);
shoppingCart.setUserId(BaseContext.getCurrentId());
shopCartMapper.updateOne(shoppingCart);
List<ShoppingCart> list = shopCartMapper.list(shoppingCart);
ShoppingCart shoppingCart1 = list.get(0);
if (shoppingCart1.getNumber()==0){
shopCartMapper.deleteById(shoppingCart1.getId());
}
}
这里做了判断,更改完数量后,如果数量为0就直接删除;
mapper:
java
<update id="updateOne">
update sky_take_out.shopping_cart
set number=number - 1
<where>
<if test="dishId!=null">
dish_id=#{dishId}
</if>
<if test="setmealId!=null">
setmeal_id=#{setmealId}
</if>
<if test="dishFlavor!=null">
and dish_flavor=#{dishFlavor}
</if>
and user_id=#{userId}
</where>
</update>
更改数量
java
@Delete("delete from sky_take_out.shopping_cart where id=#{id}")
void deleteById(Long id);
删除数量为0的商品;
总结
今天学习了springcache进行注解缓存开发,然后利用redis缓存菜品和套餐,添加购物车,查看购物车等功能;