springcache学习——@CacheEvict使⽤详解
1. 功能说明
除了填充缓存,spring cache 也⽀持使⽤ @CacheEvict 来删除缓存。@CacheEvict 就是⼀个触发器,在每次调⽤被它注解的⽅法时,就会触发删除它指定的缓存的动作。跟 @Cacheable 和 @CachePut ⼀样,@CacheEvict 也要求指定⼀个或多个缓存,也指定⾃定义⼀的缓存解析器和 key ⽣成器,也⽀持指定条件(condition 参数)。
关于这些(value、cacheNames、key、keyGenerator、cacheManager、cacheResolver、condition)属性参数的⽤法和说明,请参考:
2. 例⼦
我们来举个简单的例⼦,⾸先看⼀下 mysql 数据,我们想要删除 name=“微服务测试0修改⼀下试试” 这条数据(逻辑删除)
这条数据在 redis 中已经存在了
接下来我们编写⼀个删除⽅法,并使⽤ @CacheEvict 注解:
@Override
@CacheEvict(value = "menuById", key = "#id")
public Boolean deleteById(String id) {
veById(id);
}
然后在 controller 调⽤它:
@DeleteMapping("/deleteById/{id}")
public Boolean deleteById(@PathVariable("id")String id){
cacheablereturn 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 = veById(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 = veById(id);
System.out.println("db 操作结束");
return result;
}
接下来,我们请求接⼝删除 name=“微服务测试2” 这条数据。 返回是这样的:
打印是这样的:
数据库结果,没有删除:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论