Spring Boot + JPA2 + Hibernate - enable second level cache

Daimon picture Daimon · Jul 23, 2015 · Viewed 57.1k times · Source

I'm using Spring Boot 1.2.5 with JPA2 to annotate entities (and hibernate as underlaying JPA implementation).

I wanted to use second level cache in that setup, so entities were annotated with @javax.persistence.Cacheable

I also added following in application.properties:

spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory

During bootup hibernate complained about lack of EhCacheRegionFactory so I also added this to pom:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
</dependency>

But still queries like entityManager.find(Clazz.class, pk) are firing DB query instead of using cached data.

Any idea what is missing?

Answer

Michał Stochmal picture Michał Stochmal · Feb 19, 2019

To sum everything (L2 cache and query cache) up:

The first thing to do is to add cache provider (I recommend using EhCache) to your classpath.

Hibernate < 5.3

Add the hibernate-ehcache dependency. This library contains EhCache 2 which is now discontinued.

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
    <version>your_hibernate_version</version>
</dependency>

Hibernate >=5.3

In newer versions of Hibernate caches implementing JSR-107 (JCache) API should be used. So there're 2 dependencies needed - one for JSR-107 API and the second one for the actual JCache implementation (EhCache 3).

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-jcache</artifactId>
     <version>your_hibernate_version</version>
</dependency>

<dependency>
    <groupId>org.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>3.6.3</version>
    <scope>runtime</scope>
</dependency>

Now let's move on to application.properties/yml file:

spring:
  jpa:
    #optional - show SQL statements in console. 
    show-sql: true 
    properties:
      javax:
        persistence:
          sharedCache: 
            #required - enable selective caching mode - only entities with @Cacheable annotation will use L2 cache.
            mode: ENABLE_SELECTIVE 
      hibernate:
        #optional - enable SQL statements formatting.
        format_sql: true 
        #optional - generate statistics to check if L2/query cache is actually being used.
        generate_statistics: true
        cache:
          #required - turn on L2 cache.
          use_second_level_cache: true
          #optional - turn on query cache.
          use_query_cache: true 
          region:
            #required - classpath to cache region factory.
            factory_class: org.hibernate.cache.ehcache.EhCacheRegionFactory 

For EhCache 3 (or Hibernate >=5.3) this region factory should be used:

factory_class: org.hibernate.cache.jcache.JCacheRegionFactory

You can also enable TRACE level logging for Hibernate to verify your code and configuration:

logging:
  level:
    org:
      hibernate:
        type: trace

Now let's move on to the code. To enable L2 caching on your entity you need to add those two annotations:

@javax.persistence.Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE) //Provide cache strategy.
public class MyEntity {
  ...
}

Note - if you want to cache your @OneToMany or @ManyToOne relation - add @Cache annotation over this field as well.

And to enable query cache in your spring-data-jpa repository you need to add proper QueryHint.

public class MyEntityRepository implements JpaRepository<MyEntity, Long> {

  @QueryHints(@QueryHint(name = org.hibernate.annotations.QueryHints.CACHEABLE, value = "true"))
  List<MyEntity> findBySomething(String something);

}

Now verify via logs if your query is executed only once and remember to turn off all the debug stuff - now you're done.

Note 2 - you can also define missing cache strategy as create if you want to stay with defaults without getting warnings in your logs:

spring:
  jpa:
    properties:
      hibernate:
        javax:
          cache:
            missing_cache_strategy: create