Spring cache / jsr107:list / collection参数作为键

时间:2016-05-20 09:07:35

标签: java spring caching jcache jsr107

我有一个服务,它调用外部系统通过其外部id检索某种对象,并将它们提交回更新。而不是逐个检索对象,有一个更通用的目的方法:

public interface ExternalSystem {
    List<ExternalDTO> getObjects(List<String> externalIds);

    void updateObjects(List<ExternalDTO> updates);
}

我想在ExternalSystem调用之上放置一个缓存,因为它们非常昂贵。

在服务的实现中,我可以简单地使用spring注释:

@Cacheable("cache-external")
List<ExternalDTO> getObjects(List<String> externalIds) {} 

@CacheEvict(cacheNames="cache-external", allEntries=true)
void updateObjects(List<ExternalDTO> updates);

但是,如果我在externalIds之间有很多交集,那么这样的缓存会表现得非常糟糕,即

  1. 调用#1 getObjects([1,2,3,4]) - &gt;缓存由[1,2,3,4]键
  2. 调用#2 getObjects([1,2,3,4,5]) - &gt;缓存由[1,2,3,4,5]键
  3. 调用#3 getObjects([6,7,8,9]) - &gt;缓存[6,7,8,9]密钥
  4. 调用#4 updateObjects(1) - &gt;驱逐所有缓存,但第三个缓存不包含3
  5. 所以,问题是如何实现自定义策略(我认为它不是开箱即用的),这将只驱逐那些真正应该被驱逐的条目,并使密钥成为一种方式从缓存中检索交叉对象?

    更新。我发现了两个类似的问题:

    1. spring-cache-abstraction-with-multi-value-queries
    2. using-spring-cache-on-methods-that-take-an-array-or-collection
    3. spring-cacheable-methods-with-lists
    4. UPD2。 这是类似于我想要的东西,除了我将为集合中的每个项目放入String和ExternalDTO的缓存对。 element-level-caching-of-list-to-list

2 个答案:

答案 0 :(得分:0)

AFAIK使用注释无法做到这一点。您可以使用命令式API,其中包含您需要的批量操作,例如Cache.getAll(keySet)Cache.removeAll(keySet)

答案 1 :(得分:0)

对我来说,此配置可以正常工作。 这是我的代码的混淆版本。

@Cacheable(cacheNames = "test", key = "#p0")
public List<String> getTestFunction(List<String> someIds) {
getTestFunction(Arrays.asList("A","B","C"));
2020-04-02 15:12:35.492 TRACE 18040 --- [Test worker] o.s.cache.interceptor.CacheInterceptor   : Computed cache key '[A, B, C]' for operation Builder[public java.util.List org.Main.getTestFunction(java.util.List)] caches=[test] | key='#p0' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'

您看到它隐含了字符串

... Computed cache key '[A, B, C]' ...

我的设置: /resources/ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
    <cache name="test"
           maxBytesLocalHeap="1M"
           timeToLiveSeconds="300"/>
</ehcache>

gradle.build

plugins {
    id "org.springframework.boot" version "2.2.4.RELEASE"
    ....
}
dependencies {
    implementation "org.springframework.boot:spring-boot-starter-cache"
    implementation "org.ehcache:ehcache:3.8.1"
    ...
}