前言
👏作者簡介:我是笑霸final,一名熱愛技術的在校學生。
📝個人首頁:個人首頁1 || 笑霸final的首頁2
📕系列專欄::本文寫在java專欄
📧如果文章知識點有錯誤的地方,請指正!和大家一起學習,一起進步👀
🔥如果感覺部落客的文章還不錯的話,👍點贊👍 + 👀關注👀 + 🤏收藏🤏
🐉擷取代碼 通路gitee:gitee連結
文章目錄
- 一、環境搭建
- 二、緩存驗證碼
- 三、緩存菜品資料
- 四、用springCache 優化 套餐資料
- springCache的使用步驟
- 緩存套餐資料
一、環境搭建
- 加入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;
}
}
二、緩存驗證碼
思路:
- 在服務端
中注入 RedisTemplate對象,用來操作redis
UserController
@Autowired
private RedisTemplate redisTemplate;
- 在服務端
的sendMsg方法中 講随機生成的驗證碼緩存到redis中,并設定有效期5分鐘。
UserController
//将生成的驗證碼緩存到redis中,并設定有效期5分鐘
redisTemplate.opsForValue().set(phone,code,5, TimeUnit.MINUTES);
- 在服務端
的login方法中,從redis擷取驗證碼。登陸成功就删除redis中的驗證碼
UserController
//從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
- 改造
的list方法,先從redis中擷取菜品資料,如果有則直接傳回,無需查詢資料庫;如果沒有資料,則查詢資料庫,并将結果放入redis中
dishController
- 改造
的save和update方法,加入清理緩存邏輯(保證資料一緻)
dishController
@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的參數如下(其他的注解也大同小異)
-
:兩個等同的參數(cacheNames為Spring 4新增,作為value的别名),用于指定緩存存儲的集合名。由于Spring 4中新增了@CacheConfig,是以在Spring 3中原本必須有的value屬性,也成為非必需項了
value、cacheNames
-
:緩存對象存儲在Map集合中的key值,非必需,預設按照函數的所有參數組合作為key值,若自己配置需使用SpEL表達式,比如:@Cacheable(key = “#p0”):使用函數第一個參數作為緩存的key值,更多關于SpEL表達式的詳細内容可參考官方文檔
key
-
:緩存對象的條件,非必需,也需使用SpEL表達式,隻有滿足表達式條件的内容才會被緩存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示隻有當第一個參數的長度小于3的時候才會被緩存。
condition
-
:另外一個緩存條件參數,非必需,需使用SpEL表達式。它不同于condition參數的地方在于它的判斷時機,該條件是在函數被調用之後才做判斷的,是以它可以通過對result進行判斷。
unless
- keyGenerator:用于指定key生成器,非必需。若需要指定一個自定義的key生成器,我們需要去實作org.springframework.cache.interceptor.KeyGenerator接口,并使用該參數來指定。需要注意的是:該參數與key是互斥的
緩存套餐資料
public class R<T> implements Serializable
- 在
的list方法上加入
SetmealController
@Cacheable
- 在
的save和delete、saveUpdate、stop方法上加入
SetmealController
@CacheEvict
@CacheEvict(value = "setmealCache" ,allEntries = true)//allEntries = true清理setmealCache下所有的緩存
@Cacheable(value = "setmealCache",key = "#setmeal.categoryId")