Подтвердить что ты не робот

@Cacheable для нескольких аргументов метода

Из spring документации:

@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

Как я могу указать @Cachable для использования isbn и checkWarehouse в качестве ключа?

4b9b3361

Ответ 1

Обновить. Текущая реализация кэша Spring использует все параметры метода в качестве ключа кеша, если не указано иное. Если вы хотите использовать выбранные ключи, обратитесь к Arjan answer, который использует список Spel {#isbn, #includeUsed}, который является самым простым способом создания уникальных ключей.

От Spring Документация

Стратегия генерации ключа по умолчанию изменилась с выпуском Spring4.0. В более ранних версиях Spring использовалась стратегия генерации ключей, которая для нескольких ключевых параметров учитывала только hashCode() параметры и не равно(); это может вызвать неожиданный ключ (см. SPR-10237 для фона). Новый "SimpleKeyGenerator" использует составной ключ для таких сценариев.

До Spring 4.0

Я предлагаю вам согласовать значения параметров в выражении Spel с чем-то вроде key="#checkWarehouse.toString() + #isbn.toString()"), я считаю, что это должно работать как org.springframework.cache.interceptor.ExpressionEvaluator возвращает Object, который позже используется как ключ, так что вы не нужно предоставлять int в выражении SPEL.

Что касается хэш-кода с высокой вероятностью столкновения - вы не можете использовать его в качестве ключа.

Кто-то из этого потока предложил использовать T(java.util.Objects).hash(#p0,#p1, #p2), но он НЕ РАБОТАЕТ, и этот подход легко сломать, например, я использовал данные из SPR-9377:

    System.out.println( Objects.hash("someisbn", new Integer(109), new Integer(434)));
    System.out.println( Objects.hash("someisbn", new Integer(110), new Integer(403)));

Обе строки печатают -636517714 в моей среде.

P.S. Фактически в справочной документации мы

@Cacheable(value="books", key="T(someType).hash(#isbn)") 
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)

Я думаю, что этот пример НЕПРАВИЛЬНЫЙ и вводящий в заблуждение и должен быть удален из документации, поскольку ключи должны быть уникальными.

P.P.S. также см. https://jira.springsource.org/browse/SPR-9036 для некоторых интересных идей относительно генерации ключей по умолчанию.

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

Ответ 2

После некоторого ограниченного тестирования в Spring 3.2 кажется, что можно использовать список SpEL: {..., ..., ...}. Это также может включать значения null. Spring передает список в качестве ключа к реальной реализации кэша. При использовании Ehcache в какой-то момент такой вызов вызовет List # hashCode(), который учитывает все его элементы. (Я не уверен, полагается ли Ehcache только на хеш-код.)

Я использую это для общего кэша, в котором я также включаю имя ключа в ключ, который генератор ключей по умолчанию Spring не включает в себя. Таким образом, я могу легко стереть (один) кэш, не рискуя (слишком много...) сочетанием ключей для разных методов. Как:

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #isbn?.id, #checkWarehouse }")
public Book findBook(ISBN isbn, boolean checkWarehouse) 
...

@Cacheable(value="bookCache", 
  key="{ #root.methodName, #asin, #checkWarehouse }")
public Book findBookByAmazonId(String asin, boolean checkWarehouse)
...

Конечно, если это требуется многим методам, и вы всегда используете все параметры для своего ключа, то можно также определить собственный генератор ключей, который включает в себя имя класса и метода:

<cache:annotation-driven mode="..." key-generator="cacheKeyGenerator" />
<bean id="cacheKeyGenerator" class="net.example.cache.CacheKeyGenerator" />

... с:

public class CacheKeyGenerator 
  implements org.springframework.cache.interceptor.KeyGenerator {

    @Override
    public Object generate(final Object target, final Method method, 
      final Object... params) {

        final List<Object> key = new ArrayList<>();
        key.add(method.getDeclaringClass().getName());
        key.add(method.getName());

        for (final Object o : params) {
            key.add(o);
        }
        return key;
    }
}

Ответ 3

Вы можете использовать выражение Spring -EL, например, на JDK 1.7:

@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")

Ответ 4

Это будет работать

@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")

Ответ 5

Использовать этот

@Cacheable(value="bookCache", key="#isbn + '_' + #checkWarehouse + '_' + #includeUsed")