【问题标题】:Spring caching / spring repository, evict multiple keysSpring缓存/spring存储库,驱逐多个键
【发布时间】:2016-09-10 03:43:02
【问题描述】:

我有两种方法来获取具有两个不同参数的实体。我还有一个使用这些参数之一的保存方法。如何在两个 fetch 键下从缓存中驱逐实体?例如见下文:

@Cacheable  
public User getByUsername(String username);

@Cacheable  
public User getByEmail(String email);


@CacheEvict(key="#entity.username")
User save(User entity); 

在上面,对 getByEmail 的调用将返回过期日期。

【问题讨论】:

    标签: spring spring-data spring-cache


    【解决方案1】:

    当然,有多种选择,但像往常一样,春天支持你。

    最简单最简单的方法是在 save 方法上利用 Spring 的 @Caching 注释,就像这样......

    @Caching(evict = {
      @CacheEvict(cacheNames = "Users", key="#user.name"),
      @CacheEvict(cacheNames = "Users", key="#user.email")
    })
    User save(User user);
    

    为了您的参考,我创建了一个示例测试类来演示此工作 here

    您会注意到我在UserRepository 上使用Spring 的 Cache Abstraction 注释模仿了您上面的示例。在这种情况下,我的 repo 由 Pivotal GemFire 支持,但任何数据存储都可以使用。我使用 ConcurrentMap 作为我的缓存提供程序,使用 Spring 的 ConcurrentMapCacheManager,但当然,任何缓存提供程序都可以。

    我的test case 继续保存一个新的 User,确保用户存储在尚未缓存的状态。然后测试继续执行查询方法(findByNamefindByEmail),确保在每种情况下都适当地缓存用户实体。然后,我从底层数据存储中删除该实体,并确保该实体仍处于缓存状态。最后,在关键时刻,我修改实体,重新保存实体,断言实体是由 所有 条目已从缓存中“驱逐”出来的。

    作为另一种优化,您还可以尝试在这种情况下将@CachePut 注释与2 个@CacheEvict 注释结合起来,这可以根据新的更新实体恢复缓存,例如...

    @Caching(
      evict = {
        @CacheEvict(cacheNames = "Users", key="#a0.name", beforeInvocation = true),
        @CacheEvict(cacheNames = "Users", key="#a0.email", beforeInvocation = true)
      },
      put = {
        @CachePut(cacheNames = "Users", key="#result.name"),
        @CachePut(cacheNames = "Users", key="#result.email")
      }
    )
    User save(User user);
    

    注意:注意@CacheEvict 注释上的beforeInvocation 属性以及@CachePuts 的用户

    但是,您可能更喜欢根据需要将实体延迟添加到缓存中。

    虽然,您会假设实体被频繁访问/使用,因为您的 repo 上的 save 方法刚刚被调用,因此依赖您的基础数据存储(例如 GemFire)来设置额外的驱逐(基于溢出)/expiration(基于 LRU)设置,从而更好地管理您的系统资源(例如内存),同时仍保持最佳应用程序性能。

    值得深思。

    希望这会有所帮助。

    【讨论】:

    • 谢谢,这看起来不错。问题:#a0, #a1, ... 和 #p0, p1,... 有什么区别,使用方法参数 var name 'user'。 (实际上,我似乎无法让参数名称在 SpEL 中工作,我收到一个错误:“在 null 上找不到属性字段用户名”,null 是对象)。
    • 另一个想法:虽然使用#result.name 和result.email 来预填充缓存似乎很有意义,但我担心使用beforeInvocation = true 的并发问题。简单地使用 @CachePut(key="#a0.name") 等可能更安全,即 save() 实现实际更改值的影响和可能性与并发问题的影响和可能性。
    • 还有一个问题:在上面的示例中,电子邮件属性可能为空,也可能不为空。这不适用于某些缓存实现(例如 concurrentHashMap)。我希望有一个名为 'ignoreNullKeys' 的参数可以设置为 false,这反过来不会对具有空值的键执行对 'doEvict' 或 'put' 的调用。
    • 按顺序解决您的问题... #a 是按声明顺序排列的方法参数参数(就像在数组中传递并且数字是索引一样)。如果您不使用调试信息进行编译,则此约定是必要的。
    • 关于 beforeInvocation=true... 当然在多线程环境中需要考虑并发问题,但真正的缺点是在您知道是否保存之前预先驱逐条目会成功与否。其中,@CachePut 仅在调用方法成功完成时发生。
    猜你喜欢
    • 2014-10-12
    • 1970-01-01
    • 2018-03-23
    • 2018-12-16
    • 2011-11-11
    • 2019-03-04
    • 2018-05-17
    • 1970-01-01
    • 2014-11-04
    相关资源
    最近更新 更多