Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to cache results of a Spring Data JPA query method without using query cache?

I have a Spring Boot app with Spring Data JPA (hibernate backend) repository classes. I've added a couple custom finder methods, some with specific @Query annotation to tell it how to get the data. I have already set up EhCache for the hibernate 2nd level cache, but so far, the only way I can get these results caching is to enable the hibernate query cache. I'd prefer to define a specific cache and store the actual domain objects there just as if it were a normal finder. Below is my repo code:

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

And here is the "promotionServiceXrefByCustomerId" cache I defined, that is NOT being used:

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

What am I doing wrong? If I enable StandardQueryCache then this data gets cached there and hibernate does not execute a query. But when I disable the query caching, this does not get cached. What am I doing wrong here? PLEASE HELP!

like image 938
Kevin M Avatar asked Oct 07 '14 18:10

Kevin M


People also ask

Does JPA Repository cache?

JPA has 2 levels of caching. The first level of caching is the persistence context. The JPA Entity Manager maintains a set of Managed Entities in the Persistence Context.


1 Answers

The reason the code you have is not working is that @Cache is not intended to work that way. If you want to cache the results of a query method execution, the easiest way is to use 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); } 

This setup will cause results of calls to findByCustomerId(…) be cached by the customer identifier. Note, that we added an @CacheEvict to the overridden save(…) method, so that the cache we populate with the query method is evicted, whenever an entity is saved. This probably has to be propagated to the delete(…) methods as well.

Now you can go ahead an configure a dedicated CacheManager (see the reference documentation for details) to plug in whichever caching solution you prefer (using a plain ConcurrentHashMap here).

 @Configuration  @EnableCaching  class CachingConfig {     @Bean    CacheManager cacheManager() {       SimpleCacheManager cacheManager = new SimpleCacheManager();      cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("servicesByCustomerId)));       return cacheManager;    }  } 
like image 128
Oliver Drotbohm Avatar answered Oct 03 '22 22:10

Oliver Drotbohm