[springboot]结合redis详述声明式缓存注解的使用-Cacheable、CacheEvict、CachePut、Caching

一、缓存注解-增删改查

一定要把这张图理解的非常透彻,才能把缓存注解用好。
在这里插入图片描述

  • @Cacheable:针对查询方法配置,能够根据查询方法的请求参数对其结果进行缓存(完成上图中的蓝色连线箭头的缓存流程)
  • @CacheEvict:被注解的方法执行前或者执行之后,删除缓存(红色连线箭头3:让缓存失效、删除)
  • @CachePut:调用被注解的方法,对其返回结果进行缓存更新(红色连线箭头3:更新数据库后更新缓存)
  • @Caching:可以将上面三种注解,组合起来使用

1.1.单个对象的查询缓存

仍然以我们之前一直使用的ArtivleServiceImpl为例(包含增删改查方法),添加缓存注解。
@Cacheable注解的方法,在第一次被请求的时候执行方法体,并将方法的返回值放入缓存。在第二次请求的时候,由于缓存中已经包含该数据,将不执行被注解的方法的方法体,直接从缓存中获取数据。对于查询过程的缓存操作,要满足上图中的蓝色箭头线指引的操作流程,所有的操作流程只需要加上一个@Cacheable就可以实现。

public static final String CACHE_OBJECT = "article";  //缓存名称

@Cacheable(value = CACHE_OBJECT,key = "#id")   //这里的value和key参考下面的redis数据库截图理解
public ArticleVO getArticle(Long id) {
    
    
  return dozerMapper.map(articleMapper.selectById(id),ArticleVO.class);
}

需要注意的是:缓存注解的key是一个SPEL表达式,“#id”表示获取函数的参数id的值作为缓存的key值。如果参数id=1,那么最终redis缓存的key就是:“article::1”。下图是redis缓存数据库中这条缓存记录的截图:

在这里插入图片描述

1.2.集合对象的查询缓存

大家要注意ObjectList<Object>是两种不同的业务数据,所以对应的缓存也是两种缓存。注意下文中,缓存注解的key是字符串list,因为缓存注解默认使用SPEL表达式,如果我们想使用字符串需要加上斜杠。

public static final String CACHE_LIST_KEY  = "\"list\"";

@Cacheable(value = CACHE_OBJECT,key = CACHE_LIST_KEY)
public List<ArticleVO> getAll() {
    
    
  List<Article> articles = articleMapper.selectList(null);
  return DozerUtils.mapList(articles,ArticleVO.class);
}

对于查询过程的缓存操作,要满足上图中的蓝色箭头线指引的操作流程,所有的操作流程只需要加上一个@Cacheable就可以实现。目前MySQL数据库的article表有4条数据,所以缓存结果是一个包含4个article元素的数组
在这里插入图片描述

1.3.删除单个对象及其缓存

如下面的代码所示,将在函数执行成功之后删除redis key为“article::1”的缓存(假设删除id=1的记录)。

@Override
@Caching(evict = {
    
    
        @CacheEvict(value = CACHE_OBJECT,key = CACHE_LIST_KEY),   //删除List集合缓存
        @CacheEvict(value = CACHE_OBJECT,key = "#id")  //删除单条记录缓存
})
public void deleteArticle(Long id) {
    
    
  articleMapper.deleteById(id);
}
  • 执行该方法传递参数id=1,先执行方法去操作删除MySQL数据;成功之后在《1.1.单个对象的查询缓存》缓存到redis中的key为“article::1”的缓存也将被删除。
  • 任何一个artilce记录被删除,都会引起article::list缓存与MySQL数据库记录不一致的情况,所以需要把article::list集合缓存也删除掉。
  • 因为Java 语法不允许在同一个方法上使用两个同样的注解@CacheEvict,所以我们用@Caching注解把两个@CacheEvict包起来。

1.4.新增一个对象

  • 新增MySQL数据的时候新增redis缓存么?不是的,缓存是在获得查询结果时候回写到缓存里面的,不在新增的时候加缓存。
  • 新增的时候删除缓存么“?是的,因为我们缓存了List的集合,一旦新增一条记录。原来MySQL数据库有4条记录,新增之后MySQL数据库有5条记录,redis缓存数据库缓存结果”article::list“仍然有4条记录。redis缓存中的数据与MYSQL数据库中的数据不一致,所以把”article::list“缓存删掉。
@CacheEvict(value = CACHE_OBJECT,key = CACHE_LIST_KEY)   //删除List集合缓存
public void saveArticle(ArticleVO article) {
    
    
  Article articlePO = dozerMapper.map(article, Article.class);
  articleMapper.insert(articlePO);
}

执行完成上面的方法,MySQL数据库新增了一条article记录;成功之后在《1.2.集合对象的查询缓存》缓存到redis中的key为“article::list”的缓存也将被删除。

1.5.更新一个对象

注意更新对象的时候,我们在该方法上面加了两个缓存注解。

  • 下文的CachePut注解的作用是在方法执行成功之后,将其返回值放入缓存。key = "#article.getId()"表示使用参数article的id属性作为缓存key。
  • 下文的CacheEvict注解用于将“article::list”的缓存删除,因为某一条记录的数据更新,就表示原来缓存的List集合数据与MySQL数据库中的数据不一致,所以把它删除掉。缓存数据可以没有,但是不能和后端被缓存的关系数据库数据不一致。
@CachePut(value = CACHE_OBJECT,key = "#article.getId()")
@CacheEvict(value = CACHE_OBJECT,key = CACHE_LIST_KEY)
public ArticleVO updateArticle(ArticleVO article) {
    
    
  Article articlePO = dozerMapper.map(article,Article.class);
  articleMapper.updateById(articlePO);
  return article;  //为了保证一致性,最后返回的更新结果,最好从数据库去查
}

执行完成该方法,假如ArticleVO参数对象的id=1

  • MySQL数据库中的id=1的记录将被更新
  • redis数据库中”article::1“的记录也将被更新(CachePut)
  • redis数据库中”article::list“的记录将被删除(CacheEvict)

1.6.更新一个对象(另一种方法)

需要特别注意的是:如果在更新方法上使用CachePut注解,该方法一定要有数据更新之后返回值,因为返回值就是缓存值。比较简单的做法是直接将不一致的缓存删掉,而不是去更新缓存。这样操作对于程序员的要求更低,不容易出错。缓存数据可以没有,但是不能和后端被缓存的关系数据库数据不一致。

@Override
@Caching(evict = {
    
    
        @CacheEvict(value = CACHE_OBJECT,key = CACHE_LIST_KEY),   //删除List集合缓存
        @CacheEvict(value = CACHE_OBJECT,key = "#article.getId()")  //删除单条记录缓存
})
public void updateArticle(ArticleVO article) {
    
    
  Article articlePO = dozerMapper.map(article,Article.class);
  articleMapper.updateById(articlePO);
}

方法不需要有返回值。执行完成该方法,假如ArticleVO参数对象的id=1

  • MySQL数据库中的id=1的记录将被更新
  • redis数据库中”article::1“的记录将被删除
  • redis数据库中”article::list“的记录将被删除

二、缓存注解配置说明:

@Cacheable 通常应用到读取数据的查询方法上:先从缓存中读取,如果没有再调用方法获取数据,然后把数据查询结果添加到缓存中。如果缓存中查找到数据,被注解的方法将不会执行。

@Cacheable 主要的参数 参数说明 示例
value(cacheNames) 缓存的名称,在 spring 配置文件中定义,必须指定至少一个。当value为数组的时候会针对同一条记录做多个缓存。 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”})
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如: @Cacheable(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”)

@CachePut通常应用于修改方法配置,能够根据方法的请求参数对其注解的函数返回值进行缓存,和 @Cacheable 不同的是,它每次都会触发被注解方法的调用。

@CachePut 主要的参数 参数说明 示例
value(cacheNames) 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如: @Cacheable(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 例如: @Cacheable(value=”testcache”,condition=”#userName.length()>2”)

@CachEvict 通常应用于删除方法配置,能够根据一定的条件对缓存进行删除。可以清除一条或多条缓存。

@CacheEvict 主要的参数 参数说明 示例
value(cacheNames) 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @CachEvict(value=”mycache”) 或者 @CachEvict(value={”cache1”,”cache2”}
key 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 例如: @CachEvict(value=”testcache”,key=”#userName”)
condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才清空缓存 例如: @CachEvict(value=”testcache”, condition=”#userName.length()>2”)
allEntries 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 例如: @CachEvict(value=”testcache”,allEntries=true)
beforeInvocation 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 例如:@CachEvict(value=”testcache”,beforeInvocation=true)

在实际的生产环境中,没有一定之规,哪种注解必须用在哪种方法上,@CachEvict 注解通常也用于更新方法上。数据的缓存策略,要根据资源的使用方式,做出合理的缓存策略规划。保证缓存与业务数据库的数据一致性。并做好测试,对于缓存的正确使用,测试才是王道

三、缓存key的书写及取值

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/hanxiaotongtong/article/details/122893349