Существует несколько вариантов, конечно, но, как обычно, Весна имеет заднюю часть.
Самый простой и самый простой подход заключается в привлечении @Caching
аннотацию Spring на вашем методе save
, как так ...
@Caching(evict = {
@CacheEvict(cacheNames = "Users", key="#user.name"),
@CacheEvict(cacheNames = "Users", key="#user.email")
})
User save(User user);
Для справки, я создал класс пример тест демонстрирует эту рабочую here.
Вы заметите, что я подражал вашему примеру выше, используя Spring's Аннотации кэш-абстракции на моем UserRepository. В этом случае мое репо поддерживается Pivotal GemFire , но любое хранилище данных будет работать. Я использую ConcurrentMap
в качестве моего провайдера кэширования, используя Spring ConcurrentMapCacheManager, но, конечно же, любой поставщик кеширования будет делать.
Мои test case доходы сэкономить новыйUser
, гарантируя, что пользователь хранится еще не кэшируются. Затем в результате теста выполняются методы запросов (findByName
, findByEmail
), гарантирующие, что пользовательский объект кэшируется соответствующим образом в каждом случае. Затем я удаляю объект из основного хранилища данных и гарантирую, что объект все еще кэшируется. И, наконец, момент истины, я изменяю объект, повторно сохраняю объект, утверждая, что объект хранится в этом все записей были «выселены» из кеша.
Вы также можете попробовать, а другой оптимизации, чтобы объединить @CachePut
аннотацию с 2 @CacheEvict
аннотаций в этом случае, что может восстановить кэш на основе новой, обновленной сущности, что-то вроде ...
@Caching(
evict = {
@CacheEvict(cacheNames = "Users", key="#a0.name", beforeInvocation = true),
@CacheEvict(cacheNames = "Users", key="#a0.email", beforeInvocation = true)
},
put = {
@CachePut(cacheNames = "Users", key="#result.name"),
@CachePut(cacheNames = "Users", key="#result.email")
}
)
User save(User user);
Примечание: обратите внимание пользователя атрибута на @CacheEvict
аннотациях beforeInvocation
вместе с @CachePuts
Однако, вы можете предпочесть, что объект будет лениво добавлены в кэш на основе необходимости.
Хотя вы бы предположили, что объект часто обращается/используется с тех пор, как метод save
на вашем репо был просто вызван и поэтому полагается на ваш базовый хранилище данных (например, GemFire), чтобы установить дополнительное выселение (основанное на переполнении)/expiration (на основе LRU), тем самым улучшая управление системными ресурсами (например, памятью), сохраняя при этом оптимальную производительность приложения.
Пища для размышлений.
Надеюсь, это поможет.
Спасибо, это хорошо выглядит. Вопрос: в чем разница между # a0, # a1, ... и # p0, p1, ... и с использованием параметра метода var name 'user'.(На самом деле, я не могу заставить имя параметра работать в SpEL, я получаю сообщение об ошибке: «имя поля поля свойства не может быть найдено на null», нуль - объект). – Saul
Другая мысль: хотя кажется, что имеет смысл использовать # result.name и result.email для предварительного заполнения кеша, я беспокоюсь о проблемах параллелизма, используя beforeInvocation = true. Возможно, было бы безопаснее просто @CachePut (key = "# a0.name") и т. Д., То есть влияние и вероятность реализации() реализаций, фактически изменяющих значения по сравнению с воздействием и вероятностью проблемы параллелизма. – Saul
И еще один вопрос: в приведенном выше примере свойство электронной почты может быть или не быть нулевым. Это не будет работать с некоторыми реализациями кеша (например, concurrentHashMap). Мне жаль, что не было параметра «ignoreNullKeys», который я могу установить в false, что в свою очередь не выполняет вызовы «doEvict» или «put» на клавишах с нулевыми значениями. – Saul