前言

👏作者简介:我是笑霸final,一名热爱技术的在校学生。

📝个人主页:​​个人主页1​​​ || 笑霸final的主页2

📕系列专栏::本文写在java专栏

📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀

🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏

🐉获取代码 访问gitee:​​gitee链接​

文章目录

一、环境搭建

  • 加入maven坐标
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
  • 配置文件
spring:
redis:
host: **************** #ip地址
port: 6379 #端口号
password: *********** #密码
database: 0
jedis: #连接池
pool:
max-active: 20 #最大连接数,负值表示没有限制,默认8
max-wait: -1 #最大阻塞等待时间,负值表示没限制,默认-1
max-idle: 4 #最大空闲连接,默认8
min-idle: 0 #最小空闲连接,默认0
  • 配置类(不是必须的)
    用来设置序列化的
package com.xbfinal.reggie.config;

import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
* @autor 笑霸fianl~
* 欢迎访问GitHub:https://github.com/XBfinal
* 欢迎访问Gitee:https://gitee.com/XBfianl
* 欢迎访问CSDN:https://blog.csdn.net/weixin_52062043
*/
public class RedisConfig {
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();

//设置键的序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
//设置值的序列化方式
redisTemplate.setValueSerializer(new StringRedisSerializer());

redisTemplate.setConnectionFactory(connectionFactory);
return redisTemplate;
}

}

二、缓存验证码

思路:

  • 在服务端​​UserController​​ 中注入 RedisTemplate对象,用来操作redis
@Autowired
private RedisTemplate redisTemplate;
  • 在服务端​​UserController​​ 的sendMsg方法中 讲随机生成的验证码缓存到redis中,并设置有效期5分钟。
//将生成的验证码缓存到redis中,并设置有效期5分钟
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
  • 在服务端​​UserController​​的login方法中,从redis获取验证码。登陆成功就删除redis中的验证码
//从redis中获取缓存的验证码
Object codeInsession = redisTemplate.opsForValue().get(phone);
//如果登陆成功就删除验证码
redisTemplate.delete(phone);
  • 代码
@RestController
@RequestMapping("/user")
@Slf4j
public class Usercontroller {

@Autowired
private UserSerice userSerice;

@Autowired
private JavaMailSender mailSender;

@Autowired
private RedisTemplate redisTemplate;

@Value("${spring.mail.username}")
private String MyFrom;


/**
* 发送验证码
* @param user
* @return
*/
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user,
HttpSession session){
log.info("R<String> sendMsg()进来了");
//获取手机号
final String phone = user.getPhone();//就不修改实体类了把邮件写进 Phone

//判断手机号不为空
if(StringUtils.isNotEmpty(phone)) {
//生成4位验证码
final String code =
ValidateCodeUtils.generateValidateCode(4).toString();
System.out.println("==========");
System.out.println(code);//验证码
System.out.println("==========");
//***************************************************/
//创建简单邮件消息
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(MyFrom);//用自己的邮件发
//谁要接收
message.setTo(phone);
//邮件标题
message.setSubject("验证码");
//邮件内容
message.setText("【笑霸final】你的验证码为:"+code);//
try {
mailSender.send(message);
//需要保存一下验证码,后面用来验证
//session.setAttribute(phone, code);

//将生成的验证码缓存到redis中,并设置有效期5分钟
redisTemplate.opsForValue().set(phone,code,1, TimeUnit.MINUTES);

return R.success("发送成功");
} catch (MailException e) {
e.printStackTrace();
return R.error("短信发送失败");
}
}
return R.error("短信发送失败");
}

/**
* 登陆
* @param
* @param session
* @return
*/
@PostMapping("login")
public R<User> login(@RequestBody Map map,
HttpSession session){

//获取邮箱和验证码
final String phone = map.get("phone").toString();
final String code = map.get("code").toString();
//获取session的验证码并比对
//final Object codeInsession = session.getAttribute(phone);

//从redis中获取缓存的验证码
Object codeInsession = redisTemplate.opsForValue().get(phone);

if(codeInsession!=null && codeInsession.equals(code)){
//登陆成功
//查数据库,没有就存入数据库
LambdaQueryWrapper<User> queryWrapper
=new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone,phone);
User user = userSerice.getOne(queryWrapper);
if(user == null){
//新用户 入库
user = new User();
user.setPhone(phone);
user.setStatus(1);
userSerice.save(user);
}else if(user.getStatus()==0){
//账号被禁用
return R.error("账号被禁用");
}
//成功存入session
session.setAttribute("user",user.getId());

//如果登陆成功就删除验证码
redisTemplate.delete(phone);

return R.success(user);
}
return R.error("验证码和手机不匹配");
}
}

三、缓存菜品数据

思路

  • 当然 还是要中注入​​RedisTemplate​​对象
  • 改造​​dishController​​的list方法,先从redis中获取菜品数据,如果有则直接返回,无需查询数据库;如果没有数据,则查询数据库,并将结果放入redis中
  • 改造​​dishController​​的save和update方法,加入清理缓存逻辑(保证数据一致
@GetMapping("/list")
public R<List<DishDto>> listR(Dish dish){
List<DishDto> dtoList=null;
//先构造key
String key="dish"+dish.getCategoryId()+"_"+dish.getStatus();

//先从redis获取缓存数据,如果存在直接返回
dtoList= (List<DishDto>)redisTemplate.opsForValue().get(key);
if(dtoList!=null){
//说明存在
log.info("在redis中查询的数据");
return R.success(dtoList);

}


//查询条件对象
LambdaQueryWrapper<Dish> lambdaQueryWrapper
= new LambdaQueryWrapper();
lambdaQueryWrapper.eq(dish.getCategoryId()!=null,Dish::getCategoryId,dish.getCategoryId());
//添加起售状态的条件
lambdaQueryWrapper.eq(Dish::getStatus,1);
//添加排序
lambdaQueryWrapper.orderByAsc(Dish::getSort).orderByAsc(Dish::getUpdateTime);

final List<Dish> list = dishService.list(lambdaQueryWrapper);
dtoList = list.stream().map((item) -> {
DishDto dishDto = new DishDto();

BeanUtils.copyProperties(item,dishDto);

Long categoryId = item.getCategoryId();//分类id
//根据id查询分类对象
Category category = categoryService.getById(categoryId);

if(category != null){
String categoryName = category.getName();
dishDto.setCategoryName(categoryName);
}
final Long id = item.getId();//当前菜品id
LambdaQueryWrapper<DishFlavor>lambdaQueryWrapper1
=new LambdaQueryWrapper<>();
lambdaQueryWrapper1.eq(DishFlavor::getDishId,id);
final List<DishFlavor> dishFlavorList = dishFlavorService.list(lambdaQueryWrapper1);
dishDto.setFlavors(dishFlavorList);

return dishDto;
}).collect(Collectors.toList());

//redis不存在,则查询数据库并加入缓存,设置1小时的缓存时间
redisTemplate.opsForValue().set(key,dtoList,1, TimeUnit.HOURS);

return R.success(dtoList);
}

注意 :使用缓存的过程中要保证数据库和redis中的数据一致。当数据库发生变化时,需要及时清理缓存数据,在​​save和update方法​​添加如下代码

  • 清理所有菜品的缓存
//清理所有菜品的缓存
final Set keys = redisTemplate.keys("dish_*");
redisTemplate.delete(keys);
  • 精确清理
//精确清理
String key="dish"+dishDto.getCategoryId()+"_"+dishDto.getStatus();
redisTemplate.delete(key);

四、用springCache 优化 套餐数据

springCache的使用步骤

  • 第一步:导入springCache的maven坐标
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
<version>2.7.0</version>
</dependency>
  • 第二步:配置yml文件
spring:
cache:
redis:
time-to-live: 1800000 #设置缓存的有效期
type:
  • 第三步:在启动类上加入​​@EnableCaching​​注解,开启缓存功能
  • 第四步:在controller的方法上加入相对应的注解进行缓存操作

常用注解
@CacheEvict :应用到​​​移除数据​​​的方法上,如删除方法,调用方法时会从缓存中移除相应的数据
@Cacheable:应用到​​​读取数据​​​的方法上,即可缓存的方法,如查找方法,先从缓存中读取,如果没有再调用相应方法获取数据,然后把数据添加到缓存中
@CachePut :应用到​​​写数据​​的方法上,如新增/修改方法,调用方法时会自动把相应的数据放入缓存

Cacheable的参数如下(其他的注解也大同小异)

  • ​value、cacheNames​​:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
  • ​key​​:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
  • ​condition​​:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存。
  • ​unless​​:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
  • keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的

缓存套餐数据

思路:
注意返回类型为R的要实现序列化接口​​​Serializable​

public class R<T> implements Serializable
  • 在​​SetmealController​​​的list方法上加入​​@Cacheable​
  • 在​​SetmealController​​​的save和delete、saveUpdate、stop方法上加入​​@CacheEvict​

加入以下代码即可

@CacheEvict(value = "setmealCache" ,allEntries = true)//allEntries = true清理setmealCache下所有的缓存

@Cacheable(value = "setmealCache",key = "#setmeal.categoryId")