@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, де використовується список SpEL{#isbn, #includeUsed}
який є найпростішим способом створення унікальних ключів.
Стратегія генерації ключів за замовчуванням змінилася з виходом Spring 4.0. У попередніх версіях Spring використовувалася стратегія генерації ключів, яка для кількох ключових параметрів враховувала лише hashCode () параметрів, а не дорівнює (); це може спричинити несподівані зіткнення ключів (див. SPR-10237 для фону). Новий 'SimpleKeyGenerator' використовує складений ключ для таких сценаріїв.
До весни 4.0
Я пропоную вам поєднати значення параметрів у виразі Spel з чимось на зразок key="#checkWarehouse.toString() + #isbn.toString()")
, я вважаю, це повинно працювати як org.springframework.cache.interceptor.ExpressionEvaluator повертає Object, який згодом використовується як ключ, тому вам не потрібно надавати anint
в вашому вираженні 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 на моєму середовищі.
PS Насправді в довідковій документації, яку ми маємо
@Cacheable(value="books", key="T(someType).hash(#isbn)")
public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
Я вважаю, що цей приклад НЕПРАВИЛЬНИЙ та оманливий, і його слід вилучити з документації, оскільки ключі мають бути унікальними.
PPS також див. Https://jira.springsource.org/browse/SPR-9036 для деяких цікавих ідей щодо генерації ключів за замовчуванням.
Я хотів би додати заради справедливості і в розважальній математичних / комп'ютерній науці тим , що в відміну від вбудованої в хеше, використовуючи захищену криптографічний хеш - функцію , як MD5 або SHA256, з - за властивості такої функції IS абсолютно можливо для цього завдання, але обчислювати його щоразу може бути надто дорого, перевіряйте, наприклад, курс криптографії Ден Боне, щоб дізнатися більше.
T(someType).hash(#isbn)
працює навесні 3.1.1
?
{ #root.methodName, #param1, #param2 }
будь-якому випадку 3.1.1 є старою версією.
Після деякого обмеженого тестування з 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;
}
}
{..., ..., ...}
. У поточній документації сказано, що генерація ключів за замовчуванням враховуватиме всі параметри. Тож не потрібно творити CacheKeyGenerator
.
Ви можете використовувати вираз Spring-EL, наприклад, на JDK 1.7:
@Cacheable(value="bookCache", key="T(java.util.Objects).hash(#p0,#p1, #p2)")
@Cacheable(value="books", key="T(someType).hash(#isbn)") public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)
- але це, здається, неправильний та оманливий приклад
Ви можете використовувати клас Spring SimpleKey
@Cacheable(value = "bookCache", key = "new org.springframework.cache.interceptor.SimpleKey(#isbn, #checkWarehouse)")
Це спрацює
@Cacheable(value="bookCache", key="#checkwarehouse.toString().append(#isbn.toString())")