title: Hibernate Cache for Spring Boot Application (Infinispan, Hazelcast, ideally scalable)
body: <p>I have a Spring Boot application which retrieves (and stores) data via Hibernate. Hibernate is connected to a MySQL database. I optimised the  database requests slightly with some entity graphs to join some tables before retrieving them from the database. Now, I want the most common objects (which are not changed too often) to be stored in a cache. </p>
  
  <p>I tried caching via Infinispan and Hazelcast. I changed configurations here and there but somehow the entities are ALWAYS retrieved from the database.</p>
  
  <p>For Halzelcast, I added the following three dependencies in my <code>pom.xml</code>. Even though <code>hazelcast-hibernate</code> seems to be integrated in <code>hazelcast-spring</code>, I need the third dependency because otherwise the region factory would not be available.</p>
  
  <pre><code>&lt;dependency&gt;
      &lt;groupId&gt;com.hazelcast&lt;/groupId&gt;
      &lt;artifactId&gt;hazelcast&lt;/artifactId&gt;
      &lt;version&gt;3.12.2&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
      &lt;groupId&gt;com.hazelcast&lt;/groupId&gt;
      &lt;artifactId&gt;hazelcast-spring&lt;/artifactId&gt;
      &lt;version&gt;3.12.2&lt;/version&gt;
  &lt;/dependency&gt;
  &lt;dependency&gt;
      &lt;groupId&gt;com.hazelcast&lt;/groupId&gt;
      &lt;artifactId&gt;hazelcast-hibernate53&lt;/artifactId&gt;
      &lt;version&gt;1.3.2&lt;/version&gt;
  &lt;/dependency&gt;
  </code></pre>
  
  <p>The following settings should be sufficient for using Hazelcast as a Hibernate cache. But it does not work. I still see the same SQL queries in the logs like before.</p>
  
  <pre><code>spring.jpa.properties.hibernate.cache.use_second_level_cache=true
  spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastLocalCacheRegionFactory
  </code></pre>
  
  <p>The <code>HazelcastLocalCacheRegionFactory</code> should be used because the performance should be slightly better and ram is (currently) not such a huge issue. Nevertheless, I also welcome different approaches.</p>
  
  <p>The relevant entities get both annotations (<code>@Cacheable</code> and <code>@Cache</code>).</p>
  
  <pre><code>@Entity(name = "business_units")
  @Getter @Setter
  @Cacheable
  @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  public class BusinessUnit extends Auditable&lt;String&gt; implements Serializable {
  
      private static final long serialVersionUID = -6994142588281279518L;
  
      @Id
      @GeneratedValue(strategy = GenerationType.AUTO, generator = "native")
      @GenericGenerator(name = "native", strategy = "native")
      @Column(nullable = false, name = "id")
      private Integer id;
  
      @ManyToOne(fetch = FetchType.EAGER)
      @JoinColumn(name = "allocated_responsibility_id", referencedColumnName = "id")
      @org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
      private ProductManager allocatedResponsibility;
  
      // Some other attributes, getters and setters...
  
      @Override
      public boolean equals(Object o) {
          if (this == o) return true;
          if (!(o instanceof BusinessUnit)) return false;
          BusinessUnit that = (BusinessUnit) o;
          return id != null &amp;&amp; id.equals(that.id);
      }
  
      @Override
      public int hashCode() {
          return 31;
      }
  }
  </code></pre>
  
  <p>The same was also tried with Infinispan (different dependencies but same non-existing result).</p>
  
  <p>I intend to use an external provider because the app should be scalable. Without paying attention to this, the cache would not mirror the database correctly anymore.</p>
  
  <p>An embedded cache should be used because an extra server for Hazelcast or Infinispan would be too complicated to maintain.</p>
  
  <p>I have no idea why nothing changes (okay, i see that Hazelcast starts). Another idea I had is using a query cache instead of a Hibernate cache. But this would require more attention to ensure synchronous database and cache. Furthermore, the app could benefit less from this.</p>
  
  <p>Could you please tell me why it does not work and what to change that it works?</p>
  
  <h1>Update: Add Statistics</h1>
  
  <p>I tried below suggestions and added the statistics like @Nicolas suggested.</p>
  
  <p><code>spring.jpa.properties.hibernate.generate_statistics=true</code></p>
  
  <p>When I first load such a page, the data gets requested from the database and put into the cache (L2C).</p>
  
  <pre><code>1519200 nanoseconds spent acquiring 1 JDBC connections;
  0 nanoseconds spent releasing 0 JDBC connections;
  1759800 nanoseconds spent preparing 1 JDBC statements;
  4144000 nanoseconds spent executing 1 JDBC statements;
  0 nanoseconds spent executing 0 JDBC batches;
  161900 nanoseconds spent performing 3 L2C puts;
  0 nanoseconds spent performing 0 L2C hits;
  0 nanoseconds spent performing 0 L2C misses;
  0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
  0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
  </code></pre>
  
  <p>But when I access the site again (or perform any other action which should hit the cache), the data gets requested from the database again (and put into the cache).</p>
  
  <p>The logs when accessing the page again:</p>
  
  <pre><code>2019-10-08 12:51:38.399  INFO 17028 --- [nio-8080-exec-8] i.StatisticalLoggingSessionEventListener : Session Metrics {
      0 nanoseconds spent acquiring 0 JDBC connections;
      0 nanoseconds spent releasing 0 JDBC connections;
      0 nanoseconds spent preparing 0 JDBC statements;
      0 nanoseconds spent executing 0 JDBC statements;
      0 nanoseconds spent executing 0 JDBC batches;
      0 nanoseconds spent performing 0 L2C puts;
      0 nanoseconds spent performing 0 L2C hits;
      0 nanoseconds spent performing 0 L2C misses;
      0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
      0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
  }
  2019-10-08 12:51:38.404 DEBUG 17028 --- [nio-8080-exec-8] org.hibernate.SQL                        : 
      select
          businessun0_.id as id1_0_0_,
          productman1_.id as id2_9_1_,
          employee2_.id as id1_4_2_,
          businessun0_.created_by as created_2_0_0_,
          businessun0_.created_date as created_3_0_0_,
          businessun0_.last_modified_by as last_mod4_0_0_,
          businessun0_.last_modified_date_time as last_mod5_0_0_,
          businessun0_.allocated_responsibility_id as allocate7_0_0_,
          businessun0_.name as name6_0_0_,
          productman1_.employee_id as employee3_9_1_,
          employee2_.created_by as created_2_4_2_,
          employee2_.created_date as created_3_4_2_,
          employee2_.last_modified_by as last_mod4_4_2_,
          employee2_.last_modified_date_time as last_mod5_4_2_,
          employee2_.email_address as email_ad6_4_2_,
          employee2_.location as location7_4_2_,
          employee2_.name as name8_4_2_,
          employee2_.team as team9_4_2_ 
      from
          business_units businessun0_ 
      left outer join
          responsibilities productman1_ 
              on businessun0_.allocated_responsibility_id=productman1_.id 
      left outer join
          employees employee2_ 
              on productman1_.employee_id=employee2_.id
  2019-10-08 12:51:38.412  INFO 17028 --- [nio-8080-exec-8] i.StatisticalLoggingSessionEventListener : Session Metrics {
      901000 nanoseconds spent acquiring 1 JDBC connections;
      0 nanoseconds spent releasing 0 JDBC connections;
      1075000 nanoseconds spent preparing 1 JDBC statements;
      1766400 nanoseconds spent executing 1 JDBC statements;
      0 nanoseconds spent executing 0 JDBC batches;
      166800 nanoseconds spent performing 3 L2C puts;
      0 nanoseconds spent performing 0 L2C hits;
      0 nanoseconds spent performing 0 L2C misses;
      0 nanoseconds spent executing 0 flushes (flushing a total of 0 entities and 0 collections);
      0 nanoseconds spent executing 0 partial-flushes (flushing a total of 0 entities and 0 collections)
  }
  </code></pre>
  
  <h1>Update: Connect to Management Centre</h1>
  
  <p>I changed the region factory of Hazelcast to the one which is not local for being able to connect to the management centre.</p>
  
  <pre><code>spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastCacheRegionFactory
  </code></pre>
  
  <p>Now, two members get connected to the management centre (even though I start only one application).</p>
  
  <p>The statistics displayed in the logs still show pushes. However, the management center shows me that some data has been retrieved from the cache. Occasionally, I could see one cache hit instead of a JDBC statement in the statistics.</p>