前言
👏作者简介:我是笑霸final,一名热爱技术的在校学生。
📝个人主页:个人主页1 || 笑霸final的主页2
📕系列专栏::本文写在java专栏
📧如果文章知识点有错误的地方,请指正!和大家一起学习,一起进步👀
🔥如果感觉博主的文章还不错的话,👍点赞👍 + 👀关注👀 + 🤏收藏🤏
🐉获取代码 访问gitee:gitee链接
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
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 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);
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-cacheartifactId>
<version>2.7.0version>
dependency>
spring:
cache:
redis:
time-to-live: 1800000 #设置缓存的有效期
type: redis #缓存类型redis
@EnableCaching
注解,开启缓存功能常用注解
@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")
是否忘记了springboot如何操作redis??
点此跳转 《springDataRedis操作》的复习吧