title: Configuring caching for Hibernate with Spring Boot 2.1+
body: <h2>Context and question</h2>
  
  <p>I'm trying to configure EHCache with Hibernate in Spring Boot 2.2, but it seems I'm doing something wrong.
  I've looked at several tutorials and SO questions but didn't find something that matched fully my approach.</p>
  
  <p>I chose the approach of a no-XML, jcache configuration for the caching.
  However, Hibernate doesn't detect the existing cache manager (I checked and even enforced with <code>@AutoconfigureBefore</code>: the cache manager is loaded <em>before</em> the Hibernate auto-configuration).
  As a result, Hibernate creates a second <code>EhcacheManager</code> and throws several warnings such as the following:</p>
  
  <pre><code>HHH90001006: Missing cache[com.example.demo.one.dto.MyModel] was created on-the-fly. The created cache will use a provider-specific default configuration: make sure you defined one. You can disable this warning by setting 'hibernate.javax.cache.missing_cache_strategy' to 'create'.
  </code></pre>
  
  <p>I tried to use a <code>HibernatePropertiesCustomizer</code> to tell Hibernate which cache manager it should use.
  The bean is instanciated, but never called, so it loses all its appeal and purpose.</p>
  
  <p><strong>Does somebody know what I'm doing wrong and how I should intimate Hibernate to use the cache manager I already configured rather than creating his own?</strong></p>
  
  <p>I compared my configuration with the one <a href="https://www.jhipster.tech/" rel="noreferrer">JHipster</a> generates.
  It looks very similar, though their <code>HibernatePropertiesCustomizer</code> <em>is</em> called.
  I did not succeed in identifying the difference between their cache configuration and mine.</p>
  
  <h2>Notes from later tests (edits)</h2>
  
  <p>This appears tobe related to my data source configuration (see code below).
  I tried removing it and enabling my JPA configuration in a simpler way, and the <code>HibernatePropertiesCustomizer</code> is indeed called as expected.</p>
  
  <pre class="lang-java prettyprint-override"><code>@SpringBootApplication
  @EnableTransactionManagement
  @EnableJpaRepositories("com.example.demo.one.repository")
  public class DemoApplication {
  </code></pre>
  
  <p>Actually, having configured my datasources manually (because I need to handle two distinct datasources), I circumvent Spring Boot's <code>DataSourceAutoConfiguration</code>, and <strong>its <code>HibernateJpaAutoConfiguration</code> is not applied</strong>.
  This autoconfiguration is the one that applies the <code>HibernatePropertiesCustomizer</code> (rather, it calls <code>HibernateJpaConfiguration</code> to do it).
  However, I'm not sure how I should call this configuration to apply it.</p>
  
  <h2>Code samples</h2>
  
  <h3>Dependencies</h3>
  
  <p>I use the following dependencies (I let <code>spring-boot-starter-parent</code> set the versions):</p>
  
  <ul>
  <li>org.springframework.boot:spring-boot-starter-data-jpa</li>
  <li>org.springframework.boot:spring-boot-starter-cache</li>
  <li>org.hibernate:hibernate-jcache</li>
  <li>javax.cache:cache-api</li>
  <li>org.ehcache:ehcache</li>
  <li>org.projectlombok:lombok as a comfort</li>
  </ul>
  
  <h3>Cache config</h3>
  
  <pre class="lang-java prettyprint-override"><code>package com.example.demo.config;
  
  import lombok.extern.slf4j.Slf4j;
  import org.ehcache.config.builders.CacheConfigurationBuilder;
  import org.ehcache.config.builders.ExpiryPolicyBuilder;
  import org.ehcache.config.builders.ResourcePoolsBuilder;
  import org.ehcache.jsr107.Eh107Configuration;
  import org.springframework.boot.autoconfigure.AutoConfigureBefore;
  import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
  import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
  import org.springframework.cache.annotation.EnableCaching;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  
  import javax.cache.CacheManager;
  import java.time.Duration;
  
  @Configuration
  @EnableCaching
  @Slf4j
  //@AutoConfigureBefore(value = {DataSource1Config.class, DataSource2Config.class})
  public class CacheConfiguration {
  
      private static final int TIME_TO_LIVE_SECONDS = 240;
      private static final int MAX_ELEMENTS_DEFAULT = 200;
  
      // Create this configuration as a bean so that it is used to customize automatically created caches
      @Bean
      public javax.cache.configuration.Configuration&lt;Object, Object&gt; jcacheConfiguration() {
          final org.ehcache.config.CacheConfiguration&lt;Object, Object&gt; cacheConfiguration =
              CacheConfigurationBuilder
                  .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(MAX_ELEMENTS_DEFAULT))
                  .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(TIME_TO_LIVE_SECONDS)))
                  .build();
          return Eh107Configuration.fromEhcacheCacheConfiguration(
              cacheConfiguration
          );
      }
  
      @Bean
      public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(javax.cache.CacheManager cacheManager) {
          log.error("&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; customizer setup"); // Printed
          return hibernateProperties -&gt; {
              log.error("&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; customizer called"); // Not printed
  hibernateProperties.put("hibernate.javax.cache.cache_manager", cacheManager);
          };
      }
  
      @Bean
      public JCacheManagerCustomizer cacheManagerCustomizer(javax.cache.configuration.Configuration&lt;Object, Object&gt; jcacheConfiguration) {
          return cm -&gt; {
              createCache(cm, com.example.demo.one.dto.MyModel.class.getName(), jcacheConfiguration);
          };
      }
  
      private void createCache(CacheManager cm, String cacheName, javax.cache.configuration.Configuration&lt;Object, Object&gt; jcacheConfiguration) {
          javax.cache.Cache&lt;Object, Object&gt; cache = cm.getCache(cacheName);
          if (cache != null) {
              cm.destroyCache(cacheName);
          }
          cm.createCache(cacheName, jcacheConfiguration);
      }
  }
  </code></pre>
  
  <h3>Data source configuration</h3>
  
  <p>I have two data sources.
  The second one is similar to this one, minus the <code>@Primary</code> annotations.
  Removing the second datasource does not solve the issue.</p>
  
  <pre class="lang-java prettyprint-override"><code>package com.example.demo.config;
  
  import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
  import org.springframework.boot.context.properties.ConfigurationProperties;
  import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
  import org.springframework.context.annotation.Bean;
  import org.springframework.context.annotation.Configuration;
  import org.springframework.context.annotation.Primary;
  import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
  import org.springframework.orm.jpa.JpaTransactionManager;
  import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
  import org.springframework.transaction.PlatformTransactionManager;
  import org.springframework.transaction.annotation.EnableTransactionManagement;
  
  import javax.persistence.EntityManagerFactory;
  import javax.sql.DataSource;
  
  @Configuration
  @EnableTransactionManagement
  @EnableJpaRepositories(
      basePackages = "com.example.demo.one.repository",
      entityManagerFactoryRef = "dataSource1EntityManagerFactory",
      transactionManagerRef = "transactionManager1"
  )
  public class DataSource1Config {
  
      @Bean
      @Primary
      @ConfigurationProperties(prefix = "datasource.one")
      public DataSourceProperties dataSource1Properties() {
          return new DataSourceProperties();
      }
  
      @Bean
      @Primary
      public DataSource dataSource1(DataSourceProperties dataSource1Properties) {
          return dataSource1Properties.initializeDataSourceBuilder().build();
      }
  
      @Bean
      @Primary
      public LocalContainerEntityManagerFactoryBean dataSource1EntityManagerFactory(EntityManagerFactoryBuilder builder, DataSource dataSource1) {
          return builder
              .dataSource(dataSource1)
              .packages("com.example.demo.one.dto")
              .build();
      }
  
      @Bean
      @Primary
      public PlatformTransactionManager transactionManager1(EntityManagerFactory dataSource1EntityManagerFactory) {
          return new JpaTransactionManager(dataSource1EntityManagerFactory);
      }
  }
  </code></pre>
  
  <h3>application.yml</h3>
  
  <pre><code>spring:
    jpa:
      database: &lt;my-db&gt;
      hibernate:
        ddl-auto: validate
      properties:
        hibernate:
          dialect: &lt;my-dialect&gt;
          jdbc.time_zone: UTC
          javax:
            cache:
            #missing_cache_strategy: fail # Useful for testing if Hibernate creates a second cache manager
          cache:
            use_second_level_cache: true
            use_query_cache: false
            region.factory_class: jcache
  </code></pre>