2014-10-07 13 views
11

Ich habe eine Spring Boot App mit Spring Data JPA (Hibernate Backend) Repository-Klassen. Ich habe ein paar benutzerdefinierte Finder-Methoden, einige mit spezifischen @Query Annotation hinzugefügt, um es zu sagen, wie Sie die Daten erhalten. Ich habe bereits EhCache für den Ruhezustand-2nd-Level-Cache eingerichtet, aber bis jetzt ist der einzige Weg, wie ich diese Ergebnis-Caching erhalten kann, den Hibernate-Abfrage-Cache zu aktivieren. Ich würde es vorziehen, einen bestimmten Cache zu definieren und die eigentlichen Domain-Objekte dort zu speichern, als wäre es ein normaler Finder. Unten ist mein Repo-Code:Wie Zwischenergebnisse einer Spring Data JPA-Abfragemethode ohne Abfrage-Cache gespeichert werden?

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

Und hier ist die "promotionServiceXrefByCustomerId" Cache I definiert sind, dass nicht verwendet wird:

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

Was mache ich falsch? Wenn ich StandardQueryCache aktiviere, werden diese Daten dort zwischengespeichert und Hibernate führt keine Abfrage aus. Aber wenn ich das Zwischenspeichern der Abfrage deaktiviere, wird dies nicht zwischengespeichert. Was mache ich hier falsch? Bitte helfen!

+0

Warum sollte die '@ Cache' Annotation irgendetwas für eine Nicht-Entität tun? Diese Annotation soll sich auf Entitäten befinden, die sich nicht auf beliebigen Klassen oder Interfaces befinden. –

+0

Ich versuche es herauszufinden ... also würde jede Hilfe geschätzt werden. Die anderen find ... -Methoden, die im PagingAndSortingRepository angegeben sind, die zur Laufzeit von Spring erstellt werden, ermöglichen das Caching mit dem Second-Level-Caching von JPA/Hibernate. Das funktioniert einwandfrei. Aber diese Finder-Methode, die ich erstellt habe, kann ich nicht herausfinden, wie man IT in den Cache bringt ... –

+1

Ich bezweifle, dass die 'findAll' Caching ist, wenn Sie Caching-Anmerkungen auf Ihre Entitäten (wo sollten sie gehen). Wenn Sie versuchen (oder erwarten), dass eine nicht zwischenspeicherbare Entität zwischengespeichert wird, funktioniert sie nicht. In diesem Fall funktioniert nur der Abfragecache. –

Antwort

35

Der Grund dafür, dass der Code nicht funktioniert, ist, dass @Cache nicht so funktioniert. Wenn Sie die Ergebnisse der Ausführung einer Abfragemethode zwischenspeichern möchten, verwenden Sie am einfachsten Spring's 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); 
} 

wird diese Einrichtung zu den Ergebnissen der Gespräche führen findByCustomerId(…) durch die Kunden-ID im Cache gespeichert werden. Beachten Sie, dass wir der überschriebenen save(…)-Methode eine @CacheEvict hinzugefügt haben, sodass der Cache, den wir mit der Abfrage-Methode füllen, gelöscht wird, sobald eine Entität gespeichert wird. Dies muss wahrscheinlich auch auf die delete(…) Methoden propagiert werden.

Jetzt können Sie eine dedizierte CacheManager konfigurieren (Details siehe reference documentation), um die Caching-Lösung anzuschließen, die Sie bevorzugen (mit einer einfachen ConcurrentHashMap hier).

@Configuration 
@EnableCaching 
class CachingConfig { 

    @Bean 
    CacheManager cacheManager() { 

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

    return cacheManager; 
    } 
} 
+0

Danke. Ich begann diesen Weg zu gehen, stieß aber auf ein Problem, als ich mehrere Methoden in einer Klasse hatte, die ich brauchte, um Abstraktion herumzuspeichern, aber tatsächlich von einer anderen Methode in derselben Klasse angerufen wurde. Ich hätte AspectJ Route gehen müssen. Es stellte sich heraus, dass ich die Notwendigkeit, diese Daten aus geschäftlichen Gründen im Cache zu speichern, aufgegeben habe, so dass dieses "Problem" kein Problem mehr darstellt.Aber du hast recht, die beste Route ist definitiv die Spring-Cache-Abstraktion. Danke! –

+6

Führt das nicht zu allen Arten von Problemen und merkwürdigem Verhalten, wenn wir die Entitäten im Spring Cache zwischenspeichern? –

+0

Spring empfiehlt, nur konkrete Klassen zu kommentieren (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html Absatz nach "Methodensichtbarkeit und Cache-Annotationen") und ich laufe hinein Dieser Fehler bei der Verwendung auf einer Schnittstelle: "Die CGLIB-Unterklasse der Klasse [Klasse com.sun.proxy. $ Proxy180] konnte nicht generiert werden". Was ist der empfohlene Weg, um mit einer solchen Situation umzugehen? (Dies ist mit Spring 4.1.7) – dave

7

Sie müssen sich bewusst sein, dass Sie auf die Hibernate Querycache Aufgeben Ihre verantwortlich sind die Abfragen für ungültig zu machen, die veralten beim Speichern, Aktualisieren, Löschen Entitäten, die das Abfrageergebnis beeinflusst (was Oliver macht von CacheEvict Einstellung auf speichern) - was ich denke, kann ein Schmerz sein - oder zumindest müssen Sie berücksichtigen und ignorieren, wenn es nicht wirklich ein Problem für Ihr Szenario ist.