• [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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    需要注意的是:缓存注解的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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    对于查询过程的缓存操作,要满足上图中的蓝色箭头线指引的操作流程,所有的操作流程只需要加上一个@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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 执行该方法传递参数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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    执行完成上面的方法,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;  //为了保证一致性,最后返回的更新结果,最好从数据库去查
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    执行完成该方法,假如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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    方法不需要有返回值。执行完成该方法,假如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的书写及取值

    在这里插入图片描述

  • 相关阅读:
    SpringBoot_整合Thymeleaff模板引擎
    chat-gpt笔记:参数temperature与top_p
    络蛋白Casein-PEG-Alkyne炔基/Biotin生物素/NHS
    Springboot+微信小程序自习室管理系统毕业设计源码221535
    AI究竟能提升多少效率?哈佛已揭秘量化结果
    PHP 行事准则:allow_url_fopen 与 allow_url_include
    【初学者入门C语言】之运算符及表达式(二)
    Qt 常用控件按钮Button 案例分析
    CH34X linux驱动安装,参考代码例程
    目标检测及锚框、IoU
  • 原文地址:https://blog.csdn.net/hanxiaotongtong/article/details/122893349