Spring Boot + JPA2 + Hibernate - enable second level cache
Solution 1:
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 2:
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 :)