参考:
Redis详解 - SpringBoot整合Redis,RedisTemplate和注解两种方式的使用
springboot注解方式使用redis缓存
以注解的方式使用redis缓存得用法@CachePut , @CacheEvict, @Cacheable
第一步:引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--此处引用了jedis,版本号啥的没写www-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
第二步:在启动类加注解@EnableCaching
@SpringBootApplication
@EnableCaching//这里!!
@MapperScan()//这是mybatis-plus扫描的地方
public class SellApplication {
public static void main(String[] args) {
SpringApplication.run(SellApplication.class, args);
}
}
有关@EnableCaching
请看SpringBoot启动类@SpringBootApplication注解及执行流程
第三步:Spring配置(springboot不需要这不(应该)(反正找的博客用springboot的没有,应该也许自动装配了))
(1)启用缓存注解功能:
<cache:annotation-driven cache-manager=“SpringRedisCacheManager"/>
注:注解在controller中使用就配置在springMVC配置文件中,service中使用就配置在spring的配置文件中
。
(2)配置Redis连接信息:
Properties配置文件:
(3)配置ObjectMapper ,注入SpringDataRedis和RedisCacheManager:
第四步:代码中使用注解
常见注解
@Cacheable
:用于查询和添加缓存,第一次查询时候返回该方法返回值,并向Redis服务器保存数据- 属性如图:
-
以后调用该方法先从 Redis 中查是否有数据,如果有直接返回 Redis 缓存的数据,而不执行方法里的代码。如果没有则正常执行方法体中的代码。
value
或 cacheNames
属性做键,key 属性则可以看作为 value 的子键, 一个 value 可以有多个 key 组成不同值存在 Redis 服务器。
验证了下,value 和 cacheNames 的作用是一样的,都是标识主键。
两个属性不能同时定义,只能定义一个,否则会报错。
condition 和 unless 是条件 后面会讲用法。
-
@CachePut
:更新 Redis 中对应键的值。属性和 @Cacheable 相同 -
@CacheEvict
:删除 Redis 中对应键的值。
添加缓存
在需要加缓存的方法上添加注解 @Cacheable(cacheNames = "product", key = "123")
,
cacheNames 和 key 都必须填,如果不填 key ,默认的 key 是当前的方法名,更新缓存时会因为方法名不同而更新失败。
如在订单列表上加缓存
@RequestMapping(value = "/list", method = RequestMethod.GET)
@Cacheable(cacheNames = "product", key = "123")
public ResultVO list() {
// 1.查询所有上架商品
List<ProductInfo> productInfoList = productInfoService.findUpAll();
// 2.查询类目(一次性查询)
//用 java8 的特性获取到上架商品的所有类型
List<Integer> categoryTypes = productInfoList.stream().map(e -> e.getCategoryType()).collect(Collectors.toList());
List<ProductCategory> productCategoryList = categoryService.findByCategoryTypeIn(categoryTypes);
List<ProductVO> productVOList = new ArrayList<>();
//数据拼装
for (ProductCategory category : productCategoryList) {
ProductVO productVO = new ProductVO();
//属性拷贝
BeanUtils.copyProperties(category, productVO);
//把类型匹配的商品添加进去
List<ProductInfoVO> productInfoVOList = new ArrayList<>();
for (ProductInfo productInfo : productInfoList) {
if (productInfo.getCategoryType().equals(category.getCategoryType())) {
ProductInfoVO productInfoVO = new ProductInfoVO();
BeanUtils.copyProperties(productInfo, productInfoVO);
productInfoVOList.add(productInfoVO);
}
}
productVO.setProductInfoVOList(productInfoVOList);
productVOList.add(productVO);
}
return ResultVOUtils.success(productVOList);
}
报错:
java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.imooc.VO.ResultVO]
Hibernate: select productcat0_.category_id as category1_2_, productcat0_.category_name as category2_2_, productcat0_.category_type as category3_2_, productcat0_.create_time as create_t4_2_, productcat0_.update_time as update_t5_2_ from product_category productcat0_
arializationRedisSerializer.java:91)
at org.springframework.data.redis.cache.RedisCache$CacheValueAccessor.convertToBytesIfNecessary(RedisCache.java:471)
解决错误的办法就是加序列化public class ResultVO<T> implements Serializable
,让对象实现 Serializable 方法
@Data
public class ProductVO implements Serializable {
private static final long serialVersionUID = 961235512220891746L;
@JsonProperty("name")
private String categoryName;
@JsonProperty("type")
private Integer categoryType;
@JsonProperty("foods")
private List<ProductInfoVO> productInfoVOList ;
}
然后再类里面生成ID,需要用到一下这个插件,在idea里下载,重启即可
重启项目访问订单列表,在 rdm 里查看 Redis 缓存,有 product::123 说明缓存成功。
但是这里有一个很严重的问题,你把数据库的信息改了,然后刷新页面,还是原来的数据,这就是因为缓存的原因,所以我们给save更新的方法也要加个更新的处理, 接口方式。
更新缓存
在需要更新缓存的方法上加注解:
@CachePut(cacheNames = "prodcut", key = "123")
注意:
cacheNames 和 key 要跟 @Cacheable() 里的一致,才会正确更新。>
@CachePut() 和@Cacheable()
注解的方法返回值要一致
删除缓存
在需要删除缓存的方法上加注解:@CacheEvict(cacheNames = "prodcut", key = "123")
,执行完这个方法之后会将 Redis 中对应的记录删除。
其他常用功能
- cacheNames 也可以统一写在类上面,
@CacheConfig(cacheNames = "product")
,具体的方法上就不用写啦。
@CacheConfig(cacheNames = "product")
public class BuyerOrderController {
@PostMapping("/cancel")
@CachePut(key = "456")
public ResultVO cancel(@RequestParam("openid") String openid,
@RequestParam("orderId") String orderId){
buyerService.cancelOrder(openid, orderId);
return ResultVOUtils.success();
}
}
- Key 也可以动态设置为方法的参数
@GetMapping("/detail")
@Cacheable(cacheNames = "prodcut", key = "#openid")
public ResultVO<OrderDTO> detail(@RequestParam("openid") String openid,
@RequestParam("orderId") String orderId){
OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
return ResultVOUtils.success(orderDTO);
}
如果参数是个对象,也可以设置对象的某个属性为 key。比如其中一个参数是 user 对象,key 可以写成 key="#user.id"
- 缓存还可以设置条件。
设置当 openid 的长度大于3时才缓存
@GetMapping("/detail")
@Cacheable(cacheNames = "prodcut", key = "#openid", condition = "#openid.length > 3")
public ResultVO<OrderDTO> detail(@RequestParam("openid") String openid,
@RequestParam("orderId") String orderId){
OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
return ResultVOUtils.success(orderDTO);
}
还可以指定 unless 即条件不成立时缓存。#result 代表返回值,意思是当返回码不等于 0 时不缓存,也就是等于 0 时才缓存。
@GetMapping("/detail")
@Cacheable(cacheNames = "prodcut", key = "#openid", condition = "#openid.length > 3", unless = "#result.code != 0")
public ResultVO<OrderDTO> detail(@RequestParam("openid") String openid,
@RequestParam("orderId") String orderId){
OrderDTO orderDTO = buyerService.findOrderOne(openid, orderId);
return ResultVOUtils.success(orderDTO);
}