Spring Boot + JPA2 + Hibernate - enable second level cache
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?
Daimon
Updated on July 09, 2022Comments
-
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 about 8 yearsNope 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 over 7 yearsThx 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 about 7 yearsWhile 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 theehcache.xml
is not enough, you must also define the proper caches to get rid of all the warnings. -
Michael Piefel about 7 yearsTo 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 about 7 yearsIt is preferable to set
spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE
because only then will you honour your@javax.persistence.Cacheable
annotations. -
Pasha GR about 6 yearsI resolved the problem by setting this property : hibernate.cache.region.factory_class
-
Nitul about 6 yearsI 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/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is java. lang.IllegalArgumentException: No enum constant javax.persistence.SharedCacheMode.javax.persistence.SharedCacheMode.ALL
-
greperror about 4 yearshow 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 about 4 yearsAre both
hibernate-ehcache
ANDhibernate-jcache
required in thepom.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 thehibernate-jcache
. E.g.ehcache.org/documentation/2.8/integrations/hibernate.html -
Michał Stochmal about 4 years@LeO You're mixing two things. There is
hibernate-echache-2
andhibernate-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 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 about 4 yearsUhu, I see... Perhaps the wording should be even more explicit about it. Thx. One q left: why does
EhCache
know that it's used inJCache
mode and not innative
? Is it due to thehibernate-jcache
package? -
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 atcompile
scope. Actuallyhibernate-jcache
has this dependency incompiled
scope + some bonus logger andhibernate-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 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 about 4 yearsThanks! 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-core/…
-
Yevhenii Smyrnov almost 3 yearsAs far as I see, there is no need to explicitly set
hibernate.cache.region.factory_class
andhibernate.cache.region.use_second_level_cache
becauseorg.hibernate.cache.internal.RegionFactoryInitiator
will automatically do this if there is only oneRegionFactory
implementation -
Jef almost 3 yearsplease provide at least part of the detail from the link here as the link can get broken in the future.
-
Rod over 2 yearsThe link provided describes how to configure spring boot cache, not Hibernate. They are different things.
-
Indrajit Kanjilal about 2 yearsLatest 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.