2014-10-07 9 views
11

Mam aplikację Spring Boot z klasami repozytorium Spring Data JPA (hibernate backend). Dodałem kilka niestandardowych metod wyszukiwania, niektóre z konkretną adnotacją @Query, aby poinformować, jak pobrać dane. Skonfigurowałem już EhCache do hibernacji pamięci podręcznej drugiego poziomu, ale do tej pory jedynym sposobem, w jaki mogę uzyskać te buforowanie wyników, jest włączenie bufora zapytań hibernacji. Wolałbym zdefiniować konkretną pamięć podręczną i przechowywać rzeczywiste obiekty domenowe w taki sposób, jakby były zwykłym wyszukiwarką. Poniżej jest mój kod repo:Jak buforować wyniki metody zapytania Spring Data JPA bez korzystania z pamięci podręcznej zapytań?

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); 
} 

I tu jest "promotionServiceXrefByCustomerId" cache I zdefiniowane, że nie jest używane:

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

Co robię źle? Jeśli włączę StandardQueryCache, wtedy dane zostaną tam zbuforowane, a hibernacja nie wykona zapytania. Ale kiedy wyłączę buforowanie zapytań, nie zostanie to zbuforowane. Co ja tu robię źle? PROSZĘ POMÓŻ!

+0

Dlaczego adnotacja @ @ Cache powinna robić cokolwiek dla jednostki? Ta adnotacja ma się znajdować w jednostkach nie należących do dowolnych klas lub interfejsów. –

+0

Próbuję to rozgryźć ... więc każda pomoc będzie doceniona. Inne znaleźć ... metody, które są określone w PagingAndSortingRepository, które są tworzone w środowisku wykonawczym przez Spring zapewniają buforowanie przy użyciu buforowania drugiego poziomu JPA/Hibernate. To działa poprawnie. Ale ta metoda znajdowania, którą stworzyłem, nie mogę wymyślić, jak zmusić IT do buforowania ... –

+1

Wątpię, aby 'findAll' buforował, chyba że masz adnotacje buforujące na twoich jednostkach (i tam właśnie powinny iść). Jeśli próbujesz (lub spodziewasz się), że buforowany obiekt nie będzie buforowany, nie będzie działał, w takim przypadku będzie działać tylko pamięć podręczna zapytań. –

Odpowiedz

35

Powodem, dla którego twój kod nie działa, jest to, że @Cache nie jest przeznaczony do pracy w ten sposób. Jeśli chcesz buforować wyniki wykonania metody zapytania, najprostszym sposobem jest użycie Springa caching abstraction.

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); 
} 

Taka konfiguracja spowoduje Wyniki zaproszeń do findByCustomerId(…) być buforowane przez identyfikator klienta. Zauważ, że dodaliśmy @CacheEvict do przesłoniętej metody save(…), dzięki czemu pamięć podręczna, którą wypełniamy metodą zapytania, zostanie wyeksmitowana za każdym razem, gdy jednostka zostanie zapisana. Prawdopodobnie musi to być również propagowane do metod delete(…).

Teraz możesz przejść do konfiguracji dedykowanego CacheManager (szczegółowe informacje znajdują się w reference documentation), aby podłączyć dowolne rozwiązanie buforowania, które preferujesz (używając zwykłego ConcurrentHashMap tutaj).

@Configuration 
@EnableCaching 
class CachingConfig { 

    @Bean 
    CacheManager cacheManager() { 

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

    return cacheManager; 
    } 
} 
+0

Dzięki. Zacząłem iść tą drogą, ale wpadłem na problem, gdy miałem wiele metod w klasie, które musiałem mieć abstrakcję buforowania, ale zostały wywołane z innej metody w tej samej klasie. Musiałbym przejść na trasę AspectJ. Okazało się, że zrezygnowałem z konieczności buforowania tych danych z powodów biznesowych, więc ten "problem" nie stanowi już problemu.Ale masz rację, najlepsza trasa z pewnością przenosi się na abstrakcję Spring's Cache. Dzięki! –

+6

Czy nie prowadzi to do wszelkiego rodzaju problemów i dziwnych zachowań, jeśli buforujemy encje w wiosennej pamięci podręcznej? –

+0

Spring zaleca tylko opisywanie konkretnych klas (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html po "Widoczność metod i adnotacje w pamięci podręcznej") i napotykam ten błąd używa go na interfejsie: "Nie można wygenerować podklasy CGLIB klasy [class com.sun.proxy. $ Proxy180]". Jaki jest zalecany sposób radzenia sobie z taką sytuacją? (to jest ze Spring 4.1.7) – dave

7

Trzeba mieć świadomość, że rezygnując przy hibernacji QueryCache państwa są odpowiedzialne za unieważnienia zapytania, które stają się nieaktualne podczas zapisywania, aktualizowanie, usuwanie podmioty, które wpłynęły na wynik zapytania (co Oliver robi ustawiając CacheEvict na zapis) - co moim zdaniem może być uciążliwe - a przynajmniej musisz wziąć to pod uwagę i zignorować, jeśli nie jest to naprawdę problem dla twojego scenariusza.

Powiązane problemy