Close

Enabling Second-Level Cache

[Last Updated: Mar 10, 2018]

JPA specification supports a second-level cache which is managed on EntityManagerFactory level. That means entities can be cached in the second-level which can live across multiple EntityManager sessions. This helps improve performance by avoiding expensive database calls, keeping the entity data local to the application. A second-level cache is typically transparent to the application, as it is managed by the persistence provider.

Second-level cache is optional for the JPA Provider

Support for a second-level cache is not required by the Java Persistence API specification, setting the second-level cache mode in persistence.xml will have no effect if we use a persistence provider that does not implement a second-level cache. In the following example, we will use Hibernate as the JPA provider which supports second-level cache.

Enabling Second-Level cache

To enable second-level cache we have to use <shared-cache-mode> element in the persistence.xml, with one of the values as defined in SharedCacheMode enum.

In following example we will use shared-cache-mode=ALL which causes all entities and entity-related state and data to be cached.

Example

src/main/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
             xmlns="http://xmlns.jcp.org/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="example-unit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <shared-cache-mode>ALL</shared-cache-mode>
        <properties>
            <property name="javax.persistence.schema-generation.database.action" value="create"/>
            <property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
            <property name="javax.persistence.jdbc.url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"/>
            <property name="hibernate.cache.region.factory_class"
                      value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>

</persistence>

As seen above we also specified a value for hibernate.cache.region.factory_class. Hibernate can work with various cache providers, in this example we are using Ehcache

We have also enabled hibernate sql to see the behavior when second-level cache is enabled.

pom.xml

<dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-ehcache</artifactId>
   <version>5.2.12.Final</version>
</dependency>

The Entity

@Entity
public class Employee{
  @Id
  @GeneratedValue
  private long id;
  private String name;
  private String dept;
    .............
}

The main class

public class ExampleMain {
  private static EntityManagerFactory entityManagerFactory =
          Persistence.createEntityManagerFactory("example-unit");

  public static void main(String[] args) {
      try {
          persistEntity();
          loadEntity();
          loadEntity2();
      } finally {
          entityManagerFactory.close();
      }
  }

  public static void persistEntity() {
      Employee employee = new Employee();
      employee.setName("Megan");
      employee.setDept("Admin");
      EntityManager em = entityManagerFactory.createEntityManager();
      em.getTransaction().begin();
      em.persist(employee);
      em.getTransaction().commit();
      em.close();
  }

  private static void loadEntity() {
      EntityManager em = entityManagerFactory.createEntityManager();
      Map<String, Object> properties = new HashMap<>();
      properties.put("javax.persistence.cache.storeMode", CacheRetrieveMode.BYPASS);
      Employee employee = em.find(Employee.class, 1L, properties);
      System.out.println("Employee loaded: " + employee);
      em.close();
  }
}
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Employee (id bigint not null, dept varchar(255), name varchar(255), primary key (id))
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Employee (dept, name, id) values (?, ?, ?)
Employee loaded: Employee{id=1, name='Megan', dept='Admin'}
Employee loaded: Employee{id=1, name='Megan', dept='Admin'}

As seen above Hibernate did not use any 'select' statement to load Employee entity when we used find() method, that because the entity was cached at the time of 'insert' stage (when persist() was called).

Without second-level cache

Let's comment out shared-cache-mode and run our example again:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
             .....
    <persistence-unit name="example-unit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <exclude-unlisted-classes>false</exclude-unlisted-classes>
        <!--<shared-cache-mode>ALL</shared-cache-mode>-->
        <properties>
            .........
        </properties>
    </persistence-unit>
</persistence>
Hibernate: create sequence hibernate_sequence start with 1 increment by 1
Hibernate: create table Employee (id bigint not null, dept varchar(255), name varchar(255), primary key (id))
Hibernate: call next value for hibernate_sequence
Hibernate: insert into Employee (dept, name, id) values (?, ?, ?)
Hibernate: select employee0_.id as id1_0_0_, employee0_.dept as dept2_0_0_, employee0_.name as name3_0_0_ from Employee employee0_ where employee0_.id=?
Employee loaded: Employee{id=1, name='Megan', dept='Admin'}
Hibernate: select employee0_.id as id1_0_0_, employee0_.dept as dept2_0_0_, employee0_.name as name3_0_0_ from Employee employee0_ where employee0_.id=?
Employee loaded: Employee{id=1, name='Megan', dept='Admin'}

This time, two additional 'select' statements were fired for two find() method calls.

Example Project

Dependencies and Technologies Used:

  • h2 1.4.196: H2 Database Engine.
  • hibernate-core 5.2.12.Final: The core O/RM functionality as provided by Hibernate.
    Implements javax.persistence:javax.persistence-api version 2.1
  • hibernate-ehcache 5.2.12.Final: Integration for Ehcache into Hibernate as a second-level caching service.
  • JDK 1.8
  • Maven 3.3.9

Enabling Second-Level Cache Example Select All Download
  • jpa-second-level-cache-settings-example
    • src
      • main
        • java
          • com
            • logicbig
              • example
        • resources
          • META-INF
            • persistence.xml

    See Also