Skip to main content

Second-level Caching

Second level caching in JPA employs a cache between the JPA EntityManager which has its own (first-level) cache and the database.

Where an entity manager is tied to a specific persistence context and as such, the first-level cache is as well, the second-level cache bridges multiple entity managers to provide a shared cache that further reduces the load on the database.

Option
persistence.secondLevelCache ApplicationApplicationInstance

Enables second-level and query cache within Hibernate using Ehcache.

<options>
<persistence.secondLevelCache/>
</options>

Second-level cache usage

The second-level cache is used whenever an instance of a DataElement is retrieved directly by its identifying property, i.e. its database id. This can for example be done through the getDetails(DataRef) method of the

Query cache usage

To make use of the query cache, one must explicitly request a search's results to be cached. This is done through invoking SearchDetails.setAllowCaching(true) on the search details of the find action. Subsequent searches using equal search details (that is: equal finder, values, and options) will then make use of the query cache.

Required manual actions

Apart from enabling this option on the DataElements which should receive the functionality, one must also perform below actions to ensure correct performance and configuration of the second-level cache. Specifically, one must:

  • Provide the additional dependencies to the TomEE server
  • Provide a configuration for the caches

Additional runtime dependencies

To allow for Ehcache to be used as a second level cache, two dependencies should be included within the CATALINA_HOME/lib directory:

  • org.hibernate.hibernate-jcache (link): Version should match the other hibernate dependencies present in CATALINA_HOME.
  • org.ehcache.ehcache (link): Version should be after 3.x, as this includes the jcache support.
Example

The following maven pom file can be used to put these files in the right directory through its initialize goal:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.normalizedsystems</groupId>
<artifactId>extra-libraries</artifactId>
<!-- Version is not relevant as this pom file is only used to download libraries -->
<version>1.0.0-SNAPSHOT</version>

<properties>
<lib.directory>${CATALINA_HOME}/lib</lib.directory>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.2.0</version>
<executions>
<execution>
<id>copy-lib</id>
<phase>initialize</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${lib.directory}</outputDirectory>
<artifactItems>
<artifactItem>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.9.9</version>
</artifactItem>
<artifactItem>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jcache</artifactId>
<version>5.4.32.Final</version>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

Configuration for the cache

Configuration for the created caches can be supplied through an XML file, per Ehcache's documentation. Note that the second-level caches for each individual DataElement have their alias set to the complete classpath of the element's corresponding Data class. The query caches have two separate regions set by Hibernate: default-query-results-region and default-update-timestamps-region.

Example

Consider the following example of a configuration of the cache for a DataElement City which is located in the package com.demo. This example configuration provides a heap cache with 100 entries for each cache with a time to idle of 2 minutes:

<config xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">
<cache alias="com.demo.CityData" uses-template="default"/>

<cache alias="default-update-timestamps-region" uses-template="default">
<expiry/>
</cache>
<cache alias="default-query-results-region" uses-template="default" />

<cache-template name="default">
<key-type>java.lang.Object</key-type>
<value-type>java.lang.Object</value-type>
<expiry>
<tti unit="minutes">2</tti>
</expiry>
<heap unit="entries">100</heap>
</cache-template>
</config>
note
  • The default-update-timestamps-region does not contain expiry settings per Hibernate's documentation
  • Both the key and value types are java.lang.Object. This is because different entries are used by Hibernate to manage the first-level cache to the corresponding DataElements.
  • The queries are cached within a single cache (default-query-results-region) and the reserved space is therefore shared between all query results.

The location of this file should then be specified through the hibernate.javax.cache.uri property within the persistence-unit defined within the component's persistence.xml. As an example, when the above configuration is saved within the data layer under ext/jpa/resources/META-INF/ehcache.xml, the following property may be used within persistence.xml to refer to the configuration:

<property name="hibernate.javax.cache.uri" value="classpath://META-INF/ehcache.xml"/>