Spring cache/jsr107: аргумент list/collection как часть ключа

У меня есть служба, которая вызывает внешнюю систему, чтобы получить какие-то объекты по их внешнему идентификатору, а также отправить их обратно для обновления. Вместо того, чтобы извлекать объекты один за другим, есть более общие целевые методы:

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

    void updateObjects(List<ExternalDTO> updates);
}

Я хотел бы поместить кэш поверх вызовов ExternalSystem, потому что они довольно дорогие.

При реализации сервиса я могу просто поставить весенние аннотации:

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

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

Тем не менее, такой кеш будет работать очень плохо, если у меня много пересечений между externalIds, т.е.

  1. Вызовите getObjects #1 ([1,2,3,4]) -> кеш, помещенный ключом [1,2,3,4]
  2. Вызов #2 getObjects([1,2,3,4,5]) -> кеш, помещенный ключом [1,2,3,4,5]
  3. Вызовите 3 getObjects([6,7,8,9]) -> кеш, помещенный ключом [6,7,8,9]
  4. Вызвать # 4 updateObjects ( 1) -> удалить все кэши, но третий кэш не содержит 3

Итак, вопрос заключается в том, как реализовать собственную стратегию (я полагаю, что она не выполнима из коробки), которая будет исключать только те записи, которые действительно должны быть удалены, и сделает ключи таким образом, чтобы пересекающиеся объекты извлекались из кэша.?

Upd. Я нашел два похожих вопроса:

  1. пружинно-кэш-абстракция-с многозначными-запросами
  2. используя пружинный-кэш-на-методы, которые забирают ан-массив или сбор
  3. весна-кэшируемая-метода-с-списки

UPD2. Вот что-то похожее на то, что я хочу, за исключением того, что я помещу в кеш пары String и ExternalDTO для каждого элемента в коллекции. элемент уровня кэширования, из-списка к списку

1 ответ

AFAIK это невозможно с аннотациями. Вы можете использовать императивный API, который содержит необходимые вам массовые операции, например Cache.getAll(keySet) а также Cache.removeAll(keySet)

Для меня с этой конфигурацией все работало нормально. Это обфусцированная версия моего кода.

@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"
    ...
}

Другие вопросы по тегам