Hibernate L2 Cache | Ignite Documentation

Ignite Summit 2024 — Call For Speakers Now Open — Learn more

Edit

Hibernate L2 Cache

Overview

Apache Ignite can be used as a Hibernate second-Level cache, which can significantly speed-up the persistence layer of your application.

All work with Hibernate database-mapped objects is done within a session, usually bound to a worker thread or a Web session. By default, Hibernate only uses per-session (L1) cache, so, objects, cached in one session, are not seen in another. However, an L2 cache may be used, in which the cached objects are seen for all sessions that use the same L2 cache configuration. This usually gives a significantly greater performance gain, because each newly-created session can take full advantage of the data already present in L2 cache memory (which outlives any session-local L1 cache).

Ignite Cluster

While the L1 cache is always enabled and fully implemented by Hibernate internally, L2 cache is optional and can have multiple pluggable implementaions. Ignite can be easily plugged-in as an L2 cache implementation, and can be used in all access modes (READ_ONLY, READ_WRITE, NONSTRICT_READ_WRITE, and TRANSACTIONAL), supporting a wide range of related features:

  • caching to memory and disk, as well as off-heap memory.

  • cache transactions, that make TRANSACTIONAL mode possible.

  • clustering, with 2 different replication modes: REPLICATED and PARTITIONED

To start using Ignite as a Hibernate L2 cache, you need to perform 3 simple steps:

  • Add Ignite libraries to your application’s classpath.

  • Enable L2 cache and specify Ignite implementation class in L2 cache configuration.

  • Configure Ignite caches for L2 cache regions and start the embedded Ignite node (and, optionally, external Ignite nodes).

In the section below we cover these steps in more detail.

L2 Cache Configuration

Caution

The ignite-hibernate module has been moved to the Apache Ignite Extensions project since the Apache Ignite 2.14 version.

To configure Ignite with as a Hibernate L2 cache, without any changes required to the existing Hibernate code, you need to:

  • Add the ignite-hibernate module version 5.3.0, 5.1.0 or 4.2.0 as a dependency to your project depending on whether Hibernate 5 or Hibernate 4 is used. Alternatively, you can copy JAR files of the same name from {apache_ignite_release}/libs/optional to {apache_ignite_release}/libs folder if you start an Apache Ignite node from a command line.

  • Configure Hibernate itself to use Ignite as an L2 cache.

  • Configure Ignite caches appropriately.

Maven Configuration

To add Apache Ignite Hibernate integration to your project, add the following dependency to your pom.xml file:

<dependency>
  <groupId>org.apache.ignite</groupId>
  <artifactId>ignite-hibernate-ext</artifactId>
  <version>5.3.0</version>
</dependency>
<dependency>
  <groupId>org.apache.ignite</groupId>
  <artifactId>ignite-hibernate-ext</artifactId>
  <version>4.2.0</version>
</dependency>

Hibernate Configuration Example

A typical Hibernate configuration for L2 cache with Ignite would look like the one below:

<hibernate-configuration>
    <session-factory>
        ...
        <!-- Enable L2 cache. -->
        <property name="cache.use_second_level_cache">true</property>

        <!-- Generate L2 cache statistics. -->
        <property name="generate_statistics">true</property>

        <!-- Specify Ignite as L2 cache provider. -->
        <property name="cache.region.factory_class">org.apache.ignite.cache.hibernate.HibernateRegionFactory</property>

        <!-- Specify the name of the grid, that will be used for second level caching. -->
        <property name="org.apache.ignite.hibernate.ignite_instance_name">hibernate-grid</property>

        <!-- Set default L2 cache access type. -->
        <property name="org.apache.ignite.hibernate.default_access_type">READ_ONLY</property>

        <!-- Specify the entity classes for mapping. -->
        <mapping class="com.mycompany.MyEntity1"/>
        <mapping class="com.mycompany.MyEntity2"/>

        <!-- Per-class L2 cache settings. -->
        <class-cache class="com.mycompany.MyEntity1" usage="read-only"/>
        <class-cache class="com.mycompany.MyEntity2" usage="read-only"/>
        <collection-cache collection="com.mycompany.MyEntity1.children" usage="read-only"/>
        ...
    </session-factory>
</hibernate-configuration>

Here, we do the following:

  • Enable L2 cache (and, optionally, the L2 cache statistics generation).

  • Specify Ignite as L2 cache implementation.

  • Specify the name of the caching grid (should correspond to the one in Ignite configuration).

  • Specify the entity classes and configure caching for each class (a corresponding cache region should be configured in Ignite).

Ignite Configuration Example

A typical Ignite configuration for Hibernate L2 caching looks like this:

<!-- Basic configuration for atomic cache. -->
<bean id="atomic-cache" class="org.apache.ignite.configuration.CacheConfiguration" abstract="true">
    <property name="cacheMode" value="PARTITIONED"/>
    <property name="atomicityMode" value="ATOMIC"/>
    <property name="writeSynchronizationMode" value="FULL_SYNC"/>
</bean>

<!-- Basic configuration for transactional cache. -->
<bean id="transactional-cache" class="org.apache.ignite.configuration.CacheConfiguration" abstract="true">
    <property name="cacheMode" value="PARTITIONED"/>
    <property name="atomicityMode" value="TRANSACTIONAL"/>
    <property name="writeSynchronizationMode" value="FULL_SYNC"/>
</bean>

<bean id="ignite.cfg" class="org.apache.ignite.configuration.IgniteConfiguration">
    <!--
        Specify the name of the caching grid (should correspond to the
        one in Hibernate configuration).
    -->
    <property name="igniteInstanceName" value="hibernate-grid"/>
    ...
    <!--
        Specify cache configuration for each L2 cache region (which corresponds
        to a full class name or a full association name).
    -->
    <property name="cacheConfiguration">
        <list>
            <!--
                Configurations for entity caches.
            -->
            <bean parent="transactional-cache">
                <property name="name" value="com.mycompany.MyEntity1"/>
            </bean>
            <bean parent="transactional-cache">
                <property name="name" value="com.mycompany.MyEntity2"/>
            </bean>
            <bean parent="transactional-cache">
                <property name="name" value="com.mycompany.MyEntity1.children"/>
            </bean>

            <!-- Configuration for update timestamps cache. -->
            <bean parent="atomic-cache">
                <property name="name" value="org.hibernate.cache.spi.UpdateTimestampsCache"/>
            </bean>

            <!-- Configuration for query result cache. -->
            <bean parent="atomic-cache">
                <property name="name" value="org.hibernate.cache.internal.StandardQueryCache"/>
            </bean>
        </list>
    </property>
    ...
</bean>

Here, we specify the cache configuration for each L2 cache region:

  • We use PARTITIONED cache to split the data between caching nodes. Another possible strategy is to enable REPLICATED mode, thus replicating a full dataset between all caching nodes. See Cache Distribution Models for more information.

  • We specify the cache name that corresponds an L2 cache region name (either a full class name or a full association name).

  • We use TRANSACTIONAL atomicity mode to take advantage of cache transactions.

  • We enable FULL_SYNC to be always fully synchronized with backup nodes.

Additionally, we specify a cache for update timestamps, which may be ATOMIC, for better performance.

Having configured Ignite caching node, we can start it from within our code the following way:

Ignition.start("my-config-folder/my-ignite-configuration.xml");

After the above line is executed, the internal Ignite node is started and is ready to cache the data. We can also start additional standalone nodes by running the following command from console:

$IGNITE_HOME/bin/ignite.sh my-config-folder/my-ignite-configuration.xml
$IGNITE_HOME\bin\ignite.bat my-config-folder\my-ignite-configuration.xml
Note

The nodes may be started on other hosts as well, forming a distributed caching cluster. Be sure to specify the right network settings in Ignite configuration file for that.

Query Cache

In addition to L2 cache, Hibernate offers a query cache. This cache stores the results of queries (either HQL or Criteria) with a given set of parameters, so, when you repeat the query with the same parameter set, it hits the cache without going to the database.

Query cache may be useful if you have a number of queries, which may repeat with the same parameter values. Like in case of L2 cache, Hibernate relies on a 3-rd party cache implementation, and Ignite can be used as such.

Query Cache Configuration

The configuration information above totally applies to query cache, but some additional configuration and code change is required.

Hibernate Configuration

To enable query cache in Hibernate, you only need one additional line in configuration file:

<!-- Enable query cache. -->
<property name="cache.use_query_cache">true</property>

Yet, a code modification is required: for each query that you want to cache, you should enable cacheable flag by calling setCacheable(true):

Session ses = ...;

// Create Criteria query.
Criteria criteria = ses.createCriteria(cls);

// Enable cacheable flag.
criteria.setCacheable(true);

...

After this is done, your query results will be cached.

Ignite Configuration

To enable Hibernate query caching in Ignite, you need to specify an additional cache configuration:

<property name="cacheConfiguration">
    <list>
        ...
        <!-- Query cache (refers to atomic cache defined in above example). -->
        <bean parent="atomic-cache">
            <property name="name" value="org.hibernate.cache.internal.StandardQueryCache"/>
        </bean>
    </list>
</property>

Example

See a complete example that is available on GitHub and in every Apache Ignite distribution.