• spring cache 学习——@CacheEvict 使用详解


    1. 功能说明

      除了填充缓存,spring cache 也支持使用 @CacheEvict 来删除缓存。@CacheEvict 就是一个触发器,在每次调用被它注解的方法时,就会触发删除它指定的缓存的动作。跟 @Cacheable 和 @CachePut 一样,@CacheEvict 也要求指定一个或多个缓存,也指定自定义一的缓存解析器和 key 生成器,也支持指定条件(condition 参数)。

      关于这些(value、cacheNames、key、keyGenerator、cacheManager、cacheResolver、condition)属性参数的用法和说明,请参考:

        spring cache 学习 —— @Cacheable 使用详解

    2. 例子

      我们来举个简单的例子,首先看一下 mysql 数据,我们想要删除 name=“微服务测试0修改一下试试” 这条数据(逻辑删除)

        

       这条数据在 redis 中已经存在了

        

       接下来我们编写一个删除方法,并使用 @CacheEvict 注解:

    @Override
        @CacheEvict(value = "menuById", key = "#id")
        public Boolean deleteById(String id) {
            return this.removeById(id);
        }

      然后在 controller 调用它:

    @DeleteMapping("/deleteById/{id}")
        public Boolean deleteById(@PathVariable("id")String id){
            return menuService.deleteById(id);
        }

      接下来,我们请求一下接口,看看结果是否删除成功的:

        

       再看看 mysq 数据的 del 字段和 redis 是否还有数据:

        

         

       可以看到,mysql 和 缓存都删除成功了。

    3. allEntries 参数

      allEntries 是 @CacheEvict 特有的一个属性,意为是否删除整个缓存(value 或 cacheNames 指定的),默认为 false。从上述的例子中,我们可以看到,结果只删除了指定 key 的缓存数据条目,另一个没有被删除。现在我们来验证这个参数。

      首先,我们使用 @Cacheable 或者 @CachePut 让缓存产生两条数据:

        

       接下来,我们将上述删除方法的 allEntries=true,再请求一遍删除接口,结果:

        

        

       可以看到,尽管我的接口只指定一条数据删除,而且 mysql 中也确实只删除了一条数据,但是缓存中整个都被删除了,说明 allEntries 起作用了。

    4. beforeInvocation 参数

      beforeInvocation 是 @CacheEvict 中特有的一个属性,意为是否在执行对应方法之前删除缓存,默认 false(即执行方法之后再删除缓存)。首先思考一个问题,在什么情况下我们需要主动去删除缓存呢?一般来讲都是在删除数据的时候,需要主动去删除缓存。那么就存在一个问题,程序执行时顺序的,那我们到底是应该先删除缓存,再调用方法去数据库中删除;还是先从数据库中删除,完了之后再去删除对应的缓存呢?在正常情况下,这两种方式差别并不大,毕竟程序执行都是毫秒级的,顺序执行没有什么时间跨度。但是,现实环境复杂,缓存访问和 db 访问都可能会出现异常,这种情况下就有区别了:

        • 如果先删除缓存成功,然后 db 删除失败,那么接下来的查询就会直达数据库,造成压力;
        • 如果先 db 删除成功,然后删除缓存失败,那么就会造成脏缓存;

      至于该如何取舍,spring cache 通过 beforeInvocation 给开发者提供选择。

      接下来,我们来模拟这两种情况,直观地感受一下 beforeInvocation=true 或 false 的区别。虽然我的应用是在本地执行,但是 mysql 和 redis 都是远程服务器上的,所以可以通过断开网络连接的方式来模拟访问异常的情况。

      首先让缓存中产生两条数据

        

        对应的 mysql 数据:

        

      我们利用线程的 sleep 方法来延长执行时间,以便来得及进行断开网络的操作。。。。。。。。。。

      4.1. beforeInvocation=false 时,预期的结果应该是:mysql 中 del 变为了 true,但是缓存中仍然有数据。

        首先看下代码:

    @Override
        @CacheEvict(value = "menuById", key = "#id")
        public Boolean deleteById(String id) {
            System.out.println("开始操作 db");
            Boolean result = this.removeById(id);
            System.out.println("db 操作结束");
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return result;
        }

        接下来,我们请求接口删除 name=“微服务测试0修改一下试试”这条数据。

        因为中途关闭了网络,所以返回是这样的:

          

         打印是这样的:

          

         数据库结果,已经删除:

          

         缓存结果,没有删除:

          

         结果符合预期,说明当 beforeInvocation = false 时,是先执行方法,再操作缓存。

      4.2. beforeInvocation = true 时,预期的结果应该是:mysql 中 del 仍然为 false,但是缓存中已经被删除。

        首先看下代码:

    @Override
    @CacheEvict(value = "menuById", key = "#id", beforeInvocation = true)
    public Boolean deleteById(String id) {
    System.out.println("开始操作 db");
    try {
    Thread.sleep(10000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    Boolean result = this.removeById(id);
    System.out.println("db 操作结束");
    return result;
    }

        接下来,我们请求接口删除 name=“微服务测试2” 这条数据。

        返回是这样的:

          

         打印是这样的:

          

         数据库结果,没有删除:

          

        缓存结果,已经删除:

           

         结果符合预期,说明当 beforeInvocation = true 时,是先操作缓存,再执行方法。

      验证成功。

  • 相关阅读:
    Sa提权sql语句大全
    转载 使用AJAX实现动态刷新页面功能
    VirtualBox宿主机与虚拟机的数据共享数据空间
    SP_OACreate提权经验
    linux mysql启动错误
    Microsoft SQL Server SA权限最新入侵方法 不用net user添加账户
    Java中关于HashMap的使用和遍历
    转载 突破360防黑加固添加用户
    个人随想BLOG
    面试题收集
  • 原文地址:https://www.cnblogs.com/coding-one/p/12408631.html
Copyright © 2020-2023  润新知