Spring Boot Hibernate 2nd level cache with ehcache next to spring-cache with gradle build

It’s quite easy to mix up spring-boot-start-cache with 2nd level caching in hibernate. In this post I want to take down how to enable both and being able to run tests from within intellij and gradle (which isn’t the default if you just follow most tutorials).

To have proper dependencies for this i added the following lines to my build.gradle:

implementation("org.springframework.boot:spring-boot-starter-cache")
implementation("javax.cache:cache-api")
implementation("org.ehcache:ehcache")
implementation("org.hibernate:hibernate-jcache")

2nd level cache

With the dependencies in place I first added a default META-INF/ehcache-hibernate.xml file to my project:

<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 https://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 https://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<service>
<jsr107:defaults enable-management="true" enable-statistics="true"/>
</service>

<cache alias="default-query-results-region">
<expiry>
<tti>300</tti>
</expiry>
<heap>1024</heap>
</cache>

<cache alias="default-update-timestamps-region">
<expiry>
<none/>
</expiry>
<heap>4096</heap>
</cache>

</config>

Without the two caches hibernate will warn you that those are created on the fly with default settings. I kept management and statistics on but I haven’t further investigated how to access those – so disabling it would be fine as well.

With that file in place you then need to enable the cache and configure hibernate to use it. Here the relevant settings in my application.yml:

spring:
  jpa:
    properties:
      hibernate:
        javax:
          cache:
            provider: org.ehcache.jsr107.EhcacheCachingProvider
            # this style allows to run tests with gradle
            uri: ${new org.springframework.core.io.ClassPathResource("/META-INF/ehcache-hibernate.xml").getURI().toString()}
        cache:
          use_second_level_cache: true
          region:
            factory_class: jcache
          use_query_cache: true
      javax:
        persistence:
          sharedCache:
            mode: ENABLE_SELECTIVE

This enables 2nd level entity for selected types and query caching. Make sure to further investigate how to use that because just enabling it won’t do anything with these settings.

The uri may look strange to you and you might ask why not simply write classpath:/META-INF/ehcache-hibernate.xml. This was also my first attempt and this worked while running the tests with intellij junit runner. But running the tests with gradle failed. So the workaround was to use ClassPathResource from spring-core and with that in place I was able to run the tests from intellij junit runner and gradle.

Note: Tests are fine with this workaround in place but the application refuses to pick up the file when started normally. So my solution to this was to add a test profile with the workaround that overrides the normal syntax line.

application.yml has

classpath:/META-INF/ehcache-hibernate.xml

and for the profile i have application-test.yml with

${new org.springframework.core.io.ClassPathResource("/META-INF/ehcache-hibernate.xml").getURI().toString()}

Spring-Cache

Spring Cache wasn’t really used for hibernate 2nd level caching and it also wasn’t enabled so far. For enabling it I added a simple spring config class to my kotlin project:

import org.springframework.cache.annotation.EnableCaching
import org.springframework.context.annotation.Configuration

@Configuration
@EnableCaching
class SpringCacheConfig {
}

The important annotation is @EnableCaching. With ehcache as the chosen jcache implementation (same as for hibernate 2nd level cache) we need to place another cache config file to META-INF/ehcache-spring.xml:

<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 https://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 https://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<service>
<jsr107:defaults enable-management="true" enable-statistics="true"/>
</service>

</config>

I used the same file as for hibernate but omitted the two default caches.

Last step to enable this is to add this config to the application.yml:

spring:
  cache:
    jcache:
      # this syntax works for spring
      config: classpath:/META-INF/ehcache-spring.xml

In this case using the same workaround as before resulted in errors but for this setting intellij junit runner and gradle were ok with that syntax.