Spring Boot + JPA2 + Hibernate - enable second level cache

72,340

Solution 1

Well after some more digging here's what I was missing in application.properties:

spring.jpa.properties.javax.persistence.sharedCache.mode=ALL

Hope it helps someone :)

Solution 2

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

Solution 3

@Daimon I am not really sure, whether

spring.jpa.properties.javax.persistence.sharedCache.mode=ALL

is the best decision.

Quoted from Hibernate 20.2.1. Cache mappings documentation section

By default, entities are not part of the second level cache and we recommend you to stick to this setting. However, you can override this by setting the shared-cache-mode element in your persistence.xml file or by using the javax.persistence.sharedCache.mode property in your configuration.

whereas

ENABLE_SELECTIVE (Default and recommended value): entities are not cached unless explicitly marked as cacheable.

So, could it be, that you have not annotated all affected entities with @javax.persistence.Cacheable or rather @org.hibernate.annotations.Cache ? This could lead to the affect, that the Query Cache tried to look up the affected entities in the Second Level Cache without success and then started to fetch each entity by a single select.

Solution 4

Did you add

@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_ONLY) 

on the class you want to cache?

Share:
72,340
Daimon
Author by

Daimon

Updated on July 09, 2022

Comments

  • Daimon
    Daimon almost 2 years

    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?

  • Daimon
    Daimon about 8 years
    Nope it was not the case. spring.jpa.properties.javax.persistence.sharedCache.mode has to be set explicitely. Whether it's ALL or different setting that's another story and not related to this problem itself
  • Xiangyu
    Xiangyu over 7 years
    Thx Daimon, and for anyone come this far, it is worth noting that you need both configuration from the question, in addition to the configuration of this answer.
  • Michael Piefel
    Michael Piefel about 7 years
    While it is advisable to have the ehcache.xml, it is by no means necessary. Ehcache will use the default cache configuration which gives you 10.000 elements and a TTL of 120s – this is not tuned, but is a starting point that’s good enough for many. Note also that having the ehcache.xml is not enough, you must also define the proper caches to get rid of all the warnings.
  • Michael Piefel
    Michael Piefel about 7 years
    To add my two cents: With Spring Boot 1.4 and Ehcache and Hibernate 5.1 you really need at least to set the region factory and the shared-cache-mode. Even if ENABLE_SELECTIVE is documented to be the default, I needed to set it to this value exlicitly.
  • Michael Piefel
    Michael Piefel about 7 years
    It is preferable to set spring.jpa.properties.javax.persistence.sharedCache.mode=ENA‌​BLE_SELECTIVE because only then will you honour your @javax.persistence.Cacheable annotations.
  • Pasha GR
    Pasha GR about 6 years
    I resolved the problem by setting this property : hibernate.cache.region.factory_class
  • Nitul
    Nitul about 6 years
    I am getting Error with spring boot 1.5.9.RELEASE : An exception occurred while running. null: InvocationTargetException: Error creating bean with name 'entityManagerFactory' defined in class path resource [o rg/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAu‌​toConfiguration.clas‌​s]: Invocation of init method failed; nested exception is java. lang.IllegalArgumentException: No enum constant javax.persistence.SharedCacheMode.javax.persistence.SharedCa‌​cheMode.ALL
  • greperror
    greperror about 4 years
    how to add time to live for the cached entity? Also, does the second level cache evicts or delete itself by default, or does it maintain availability through the application uptime?
  • LeO
    LeO about 4 years
    Are both hibernate-ehcache AND hibernate-jcache required in the pom.xml or not? I'm confused cause I'm using Spring Boot 2.2 + Hibernate >6 and EhCache 3. I'm unclear if they would be used alternatively or as an replacement. Especially since other blogs only mention the first and don't talk about the hibernate-jcache. E.g.ehcache.org/documentation/2.8/integrations/hibernate.htm‌​l
  • Michał Stochmal
    Michał Stochmal about 4 years
    @LeO You're mixing two things. There is hibernate-echache-2 and hibernate-ehcache-3. The first one (2) was standalone cache implementation which is now obsolete. The second one (3) is an implementation of JSR-107 API (also called jcache). If you're using ehcache ver. 3 both dependencies (hibernate-jcache and hibernate-ehcache-3) are needed.
  • Michał Stochmal
    Michał Stochmal about 4 years
    @greperror second level cache evicts itself every time entity changes. In order to change time to live you'll need to provide custom cacheManager bean via @Bean public CacheManager cacheManager(). Ehcache docs about configuration of cache expiration: ehcache.org/documentation/3.8/expiry
  • LeO
    LeO about 4 years
    Uhu, I see... Perhaps the wording should be even more explicit about it. Thx. One q left: why does EhCache know that it's used in JCache mode and not in native? Is it due to the hibernate-jcache package?
  • Michał Stochmal
    Michał Stochmal about 4 years
    @LeO technically hibernate-ehcache (3) is using javax.cache:cache-api artifact at provided scope, so you must add this artifact manually at compile scope. Actually hibernate-jcache has this dependency in compiled scope + some bonus logger and hibernate-core dependencies. Just look at these maven artifacts: mvnrepository.com/artifact/org.ehcache/ehcache/3.8.1, mvnrepository.com/artifact/org.hibernate/hibernate-jcache/…
  • Alfredo Carrillo
    Alfredo Carrillo about 4 years
    @greperror you can have a xml file in order to configure your caches, lets say 'time to live' or 'time to idle'. Here you can see a good example. And you can refer to it as follows: spring: jpa: properties: hibernate: javax: cache: uri: classpath:jcache.xml
  • zeratul021
    zeratul021 about 4 years
    Thanks! This is great example and helped me with the initial setup. Just a small hint: not setting sharedCache explicitly is OK. By default un-annotated entities won't be cached. And the moment you use Hibernate's Cache annotation, that setting is mostly ignored: github.com/hibernate/hibernate-orm/blob/5.4.14/hibernate-cor‌​e/…
  • Yevhenii Smyrnov
    Yevhenii Smyrnov almost 3 years
    As far as I see, there is no need to explicitly set hibernate.cache.region.factory_class and hibernate.cache.region.use_second_level_cache because org.hibernate.cache.internal.RegionFactoryInitiator will automatically do this if there is only one RegionFactory implementation
  • Jef
    Jef almost 3 years
    please provide at least part of the detail from the link here as the link can get broken in the future.
  • Rod
    Rod over 2 years
    The link provided describes how to configure spring boot cache, not Hibernate. They are different things.
  • Indrajit Kanjilal
    Indrajit Kanjilal about 2 years
    Latest spring boot and hibernate which is by default > 5.3 will require <b>org.hibernate.cache.jcache.JCacheRegionFactory</b> to be the "factory_class". It solved my problems of getting this work.