目录
2.1、将菜品信息缓存到redis中以及从redis中查询菜品信息
当系统用户数量变多以后,会频繁地访问数据库,造成系统性能下降,因此为了解决这个问题,我们使用Redis数据库来对外卖项目进行优化。
在进行优化之前我们先将代码上传到Gitee上来对代码进行管理,有一个master分支和一个v1.0分支,接下来我主要在v1.0分支上进行代码开发。
一、环境搭建
1、maven坐标
在pom.xml中导入spring data redis的maven坐标:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、redis相关配置
在项目的application.yml中加入redis相关配置,其中密码并非必须项:
spring
redis:
host: 127.0.0.1
port: 6379
//password: 你设置的密码
database: 0
3、设置redis配置类
在com/itheima/reggie/config文件夹中创建RedisConfig.java。值得注意的是,即使我们不创建这个配置类,框架自己也会创建,我们自己创建是为了改变序列化方式。
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public RedisTemplate<Object,Object> redisTemplate(RedisConnectionFactory connectionFactory){
RedisTemplate<Object,Object> redisTemplate=new RedisTemplate<>();
//默认的Key序列化器为:JDKSerializationRedisSerializer
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}
}
4、将项目的改变同步到Gitee仓库中
之后在项目上右击,点击Git->add选项,然后选择右上角的Commit
然后编写好说明之后点击Commit and Push选项
之后选择上传到哪个分支并点击上传即可,之后不再介绍同步Gitee代码的操作。
二、缓存短信验证码
1、实现思路
2、代码实现
代码实现比较简单,首先在UserController.java中注入RedisTemplate对象;
@Autowired
private RedisTemplate redisTemplate;
然后在sendMsg方法中将生成的验证码保存到Redis中,并且设置有效期为5分钟。并将之前把验证码保存到session中的代码注释掉。
@PostMapping("/sendMsg")
public R<String> sendMsg(HttpSession session,@RequestBody User user){
//获取手机号
String phone=user.getPhone();
if(StringUtils.isNotEmpty(phone)){
//随机生成验证码
String code=ValidateCodeUtils.generateValidateCode(4).toString();
log.info("code={}",code);
//调用阿里云提供的短信服务API完成发送短信
//SMSUtils.sendMessage("阿里云短信测试","SMS_154950909",phone,code);
//需要将生成的验证码保存到Session中
//session.setAttribute(phone,code);
//需要将生成的验证码保存到Redis中,并且设置有效期为5分钟
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
return R.error("短信发送成功");
}
return R.error("短信发送失败");
}
最后在login方法中将获取验证码的方式从session改为redis,并且在登录成功之后将验证码从redis中删除
@PostMapping("/login")
public R<User> login(HttpSession session,@RequestBody Map phoneandcode){
//获取手机号
String phone=phoneandcode.get("phone").toString();
//获取验证码
String code=phoneandcode.get("code").toString();
//从Session中获取保存的验证码
//Object codeInSession=session.getAttribute(phone);
//从Redis中获取验证码
Object codeInSession=redisTemplate.opsForValue().get(phone);
//进行验证码的对比
if (codeInSession!=null && codeInSession.equals(code)){
//若比对成功,说明登陆成功
//判断当前手机号是否为新用户,如果是新用户就自动完成注册
LambdaQueryWrapper<User> lambdaQueryWrapper=new LambdaQueryWrapper();
lambdaQueryWrapper.eq(User::getPhone,phone);
User user=userService.getOne(lambdaQueryWrapper);
if(user==null){
//判断当前手机号为新用户,自动完成注册
user=new User();
user.setPhone(phone);
user.setStatus(1);
userService.save(user);
}
session.setAttribute("user",user.getId());
//如果用户登陆成功,删除Redis中缓存的验证码
redisTemplate.delete(phone);
return R.success(user);
}
return R.error("登陆失败");
}
3、测试
我们在客户登录界面获取验证码,随便写个号码为:18055123639,然后获取验证码。之后查看redis数据库,发现存在key为号码的数据
查看对应的value
由于没有对value的序列化机制进行改变,因此value仍使用的是JDK序列化机制。
然后我们登陆进去之后再查看redis数据库,就会发现key为18055123639的数据已经被删除了。
三、缓存菜品数据
1、实现思路
2、代码实现
2.1、将菜品信息缓存到redis中以及从redis中查询菜品信息
首先在DishController.java中注入RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
然后在list()方法中最前面的位置添加如下代码
List<DishDto> dishDtoList=null;
String key="dish_"+dish.getCategoryId()+"_"+dish.getStatus();
//从redis中获取缓存数据
dishDtoList= (List<DishDto>) redisTemplate.opsForValue().get(key);
if(dishDtoList!=null){
//如果存在,直接返回,无需查询数据库
return R.success(dishDtoList);
}
如果if不成立,那就会正常查询数据库,在查询结束后将查询到的菜品数据缓存到Redis
//不存在,需要查询数据库,将查询到的菜品数据缓存到Redis
redisTemplate.opsForValue().set(key,dishDtoList,60, TimeUnit.MINUTES);
2.2、清理缓存
在菜品的信息被增删改之后,我们应该将redis中的缓存进行清理,避免出现前端从redis缓存中读取过期的数据。
我们在DishController.java中的增和更新方法的参数都是DishDto类型的,我们有两种清除缓存的方法,分别是直接删除所有缓存以及只删除改变或增加的菜品所属类的缓存。
@PostMapping
public R<String> save(@RequestBody DishDto dishDto){
log.info(dishDto.toString());
dishService.saveWithFlavor(dishDto);
/*清理所有菜品的缓存数据
Set keys=redisTemplate.keys("dish_*");
redisTemplate.delete(keys);*/
//精确清理某个分类下面的菜品缓存数据
String key="dish_"+dishDto.getCategoryId()+"_1";
redisTemplate.delete(key);
return R.success("新增菜品成功");
}
@PutMapping
public R<String> update(@RequestBody DishDto dishDto){
log.info(dishDto.toString());
dishService.updateWithFlavor(dishDto);
/*清理所有菜品的缓存数据
Set keys=redisTemplate.keys("dish_*");
redisTemplate.delete(keys);*/
//精确清理某个分类下面的菜品缓存数据
String key="dish_"+dishDto.getCategoryId()+"_1";
redisTemplate.delete(key);
return R.success("修改菜品成功");
}
而删除和修改菜品方法的参数是菜品id,虽然也可以使用上述两种清除缓存的方法,但为了省事(懒),我就只提供了直接删除所有缓存的代码。
@DeleteMapping
public R<String> deleteDish(@RequestParam List<Long> ids){
log.info("要删除的菜品id为{}",ids);
dishService.deleteWithFlavorAndSetmeal(ids);
//清理所有菜品的缓存数据
Set keys=redisTemplate.keys("dish_*");
redisTemplate.delete(keys);
return R.success("删除菜品成功");
}
@PostMapping("/status/{status}")
public R<String> update_status(@RequestParam List<Long> ids){
log.info("ids={}",ids);
dishService.updateDishStatus(ids);
//清理所有菜品的缓存数据
Set keys=redisTemplate.keys("dish_*");
redisTemplate.delete(keys);
return R.success("套餐状态修改成功");
}
3、测试
3.1、添加缓存
我们登陆上订餐系统之后, 点击“湘菜”和“粤菜之后”,再查看redis数据库
可以发现菜品数据成功存进去了。
3.2、清除缓存
首先我们在客户端点击过湘菜、川菜以及粤菜之后,可以看到菜品信息以及加到redis缓存中去了。
然后我们随便对湘菜中某个菜品进行修改,川菜中某个菜品进行删除,粤菜中某个菜品进行修改状态。之后再查看redis数据库。
可以发现对应的缓存已经被清理掉了。
如果代码没问题,可以在与Gitee同步过之后切换回master分支,然后将v1.0的代码合并到master分支中,合并完成之后再切换回v1.0分支进行接下来的开发。
四、Spring Cache
1、Spring Cache介绍
2、Spring Cache常用注解
3、Spring Cache使用方式
接下来我们使用Spring Cache来实现缓存套餐数据。
五、缓存套餐数据
1、实现思路
2、代码改造
然后在SetmealController.java中的list方法上加上@Cacheable注解。
注意,这里面的value不是指key-value结构里面的value,而是指调用list方法所保存的所有缓存的集合名。key则依然是 key-value结构里面的key。
然后分别在增删改以及修改贩卖状态的方法上加上@CacheEvict注解
不过理论上删除方法可以不用加@CacheEvict注解,因为缓存中只会缓存售卖中的套餐,而删除操作只能对非售卖中的套餐操作,我这里是为了统一才加上的。
之后可以将代码push and commit到Gitee中,并将v1.0合并到master中。