@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Как я могу указать @Cachable
для использования isbn
и checkWarehouse
в качестве ключа?
@Cacheable(value="bookCache", key="isbn")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Как я могу указать @Cachable
для использования isbn
и checkWarehouse
в качестве ключа?
Обновить. Текущая реализация кэша Spring использует все параметры метода в качестве ключа кеша, если не указано иное. Если вы хотите использовать выбранные ключи, обратитесь к Arjan answer, который использует список Spel {#isbn, #includeUsed}
, который является самым простым способом создания уникальных ключей.
Стратегия генерации ключа по умолчанию изменилась с выпуском 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, из-за свойства этой функции возможны для этой задачи, но вычислять ее каждый раз может быть слишком дорого.
После некоторого ограниченного тестирования в 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;
}
}
Вы можете использовать выражение Spring -EL, например, на JDK 1.7:
@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")
Это будет работать
@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")
Использовать этот
@Cacheable(value="bookCache", key="#isbn + '_' + #checkWarehouse + '_' + #includeUsed")