Как кэшировать результаты метода запроса <JPY> данных JPA без использования кеша запросов?

У меня есть приложение загрузки Spring с Spring хранилищами данных JPA (hibernate backend). Я добавил несколько пользовательских методов поиска, некоторые с конкретной аннотацией @Query, чтобы рассказать, как получить данные. Я уже настроил EhCache для спящего режима 2-го уровня, но до сих пор единственный способ получить эти кеширование результатов - включить кеш-память спящего режима. Я бы предпочел определить конкретный кеш и сохранить реальные объекты домена там, как если бы это был обычный искатель. Ниже приведен код моего репо:

public interface PromotionServiceXrefRepository extends PagingAndSortingRepository<PromotionServiceXref, Integer> {

  @Query("SELECT psx FROM Customer c " +
         "JOIN c.customerProductPromotions cpp " +
         "JOIN cpp.productPromotion pp " +
         "JOIN pp.promotion p JOIN p.promotionServiceXrefs psx " +
         "WHERE c.customerId = ?1")
  @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true"))
  @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "promotionServiceXrefByCustomerId")
  Set<PromotionServiceXref> findByCustomerId(int customerId);
}

И вот я определил кеш "promotionServiceXrefByCustomerId", который НЕ используется:

<cache name="promotionServiceXrefByCustomerId" overflowToDisk="true" diskPersistent="true"
       maxEntriesLocalHeap="3000000" eternal="true" diskSpoolBufferSizeMB="20" memoryStoreEvictionPolicy="LFU"
       transactionalMode="off" statistics="true">
</cache>

Что я делаю неправильно? Если я включу StandardQueryCache, тогда эти данные будут кэшироваться там, а спящий режим не выполнит запрос. Но когда я отключу кеширование запросов, это не кэшируется. Что я здесь делаю неправильно? ПОЖАЛУЙСТА, ПОМОГИТЕ!

Ответ 1

Причина, по которой ваш код не работает, заключается в том, что @Cache не предназначен для работы таким образом. Если вы хотите кэшировать результаты выполнения метода запроса, самый простой способ - использовать Spring кэширование абстракции.

interface PromotionServiceXrefRepository extends PagingAndSortingRepository<PromotionServiceXref, Integer> {

  @Query("…")
  @Cacheable("servicesByCustomerId")
  Set<PromotionServiceXref> findByCustomerId(int customerId);

  @Override
  @CacheEvict(value = "servicesByCustomerId", key = "#p0.customer.id")
  <S extends PromotionServiceXref> S save(S service);
}

Эта настройка приведет к кэшированию результатов вызовов findByCustomerId(…) с помощью идентификатора клиента. Обратите внимание, что мы добавили @CacheEvict к переопределенному методу save(…), так что кеш, который мы заполняем методом запроса, вызывается, когда объект сохраняется. Вероятно, это необходимо распространить и на методы delete(…).

Теперь вы можете настроить специальную CacheManager (подробнее см. справочную документацию), чтобы подключить любое решение для кеширования, которое вы предпочитаете (здесь используется простая ConcurrentHashMap).

 @Configuration
 @EnableCaching
 class CachingConfig {

   @Bean
   CacheManager cacheManager() {

     SimpleCacheManager cacheManager = new SimpleCacheManager();
     cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("servicesByCustomerId)));

     return cacheManager;
   }
 }

Ответ 2

Вам нужно знать, что, отказываясь от Hibernate QueryCache, вы несете ответственность за недействительность запросов, которые становятся устаревшими при сохранении, обновлении, удалении объектов, которые повлияли на результат запроса (что делает Оливер, устанавливая CacheEvict при сохранении) - который, я думаю, может быть болью или, по крайней мере, вам нужно учесть и игнорировать его, если это не проблема для вашего сценария.