[id='integrating_jpa_hibernate']
= JPA/Hibernate 2L Cache

Hibernate manages a second-level cache where it moves data into and out as a result of operations performed by `Session` or `EntityManager` (JPA).
The second-level cache is pluggable via an SPI which {brandname} implements.
This enables {brandname} to be used as second-level cache for Hibernate.

link:https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#caching[Hibernate documentation]
contains a lot of information about second-level cache, types of caches...etc.
This chapter focuses on what you need to know to use {brandname} as second-level cache provider with Hibernate.

Applications running in environments where {brandname} is not default cache provider for Hibernate will need to depend on the correct cache provider version.

The {brandname} cache provider version suitable for your application depends on the Hibernate version in use:

Hibernate 5.3::
Use the following Maven coordinates:
+
[source,xml,options="nowrap",subs=attributes+]
----
include::dependencies_maven/hibernate_v5.3.xml[]
----

Hibernate 5.2::
+
[NOTE]
====
Hibernate 5.2 is supported in {brandname} 9.2.x only.
====
+
Use the following Maven coordinates:
+
[source,xml,options="nowrap",subs=attributes+]
----
include::dependencies_maven/hibernate_v5.2.xml[]
----

Hibernate 5.1::
Use the following Maven coordinates:
+
[source,xml,options="nowrap",subs=attributes+]
----
include::dependencies_maven/hibernate_v5.1.xml[]
----

[NOTE]
====
Hibernate version 5.0 and earlier: the {brandname} cache provider is shipped by Hibernate.
Documentation and Maven coordinates are located in the
link:https://docs.jboss.org/hibernate/orm/5.0/userguide/html_single/Hibernate_User_Guide.html#caching-provider-infinispan[Hibernate documentation].
====

Apart from {brandname} specific configuration, it's worth noting that enabling second cache requires some changes to the descriptor file
(`persistence.xml` for JPA or `application.properties` for Spring).
To use second level cache, you first need to enable the second level cache so that entities and/or collections can be cached:

.Enable second-level cache
[cols="1,10"]
|===
| JPA       | `<property name="hibernate.cache.use_second_level_cache" value="true"/>`
| Spring    | `spring.jpa.properties.hibernate.cache.use_second_level_cache=true`
|===

To select which entities/collections to cache, first annotate them with `javax.persistence.Cacheable`.
Then make sure shared cache mode is set to `ENABLE_SELECTIVE`:

.Enable selective shared cached mode
[cols="1,10"]
|===
| JPA       | `<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>`
| Spring    | `spring.jpa.properties.javax.persistence.sharedCache.mode=ENABLE_SELECTIVE`
|===

NOTE: This is the most common way of selecting which entities/collections to cache.
However, there are alternative ways to which are explained in the
link:https://docs.jboss.org/hibernate/orm/5.2/userguide/html_single/Hibernate_User_Guide.html#caching-mappings[Hibernate documentation].

Optionally, queries can also be cached but for that query cache needs to be enabled:

.Enable query cache
[cols="1,10"]
|===
| JPA       | `<property name="hibernate.cache.use_query_cache" value="true"/>`
| Spring    | `spring.jpa.properties.hibernate.cache.use_query_cache=true`
|===

NOTE: As well as enabling query cache, forcing a query to be cached requires the query to be made cacheable.
For example, for JPA queries: `query.setHint("org.hibernate.cacheable", Boolean.TRUE)`.

The best way to find out whether second level cache is working or not is to inspect the statistics.
By inspecting the statistics you can verify if the cache is being hit, if any new data is stored in cache...etc.
Statistics are disabled by default, so it is recommended that you enable statistics:

.Enable statistics
[cols="1,10"]
|===
| JPA       | `<property name="hibernate.generate_statistics" value="true" />`
| Spring    | `spring.jpa.properties.hibernate.generate_statistics=true`
|===


== Deployment Scenarios

How to configure {brandname} to be the second level cache provider varies slightly depending on the deployment scenario:

=== Single-Node Standalone Hibernate Application

In standalone library mode, a JPA/Hibernate application runs inside a Java SE application or inside containers that don’t offer {brandname} integration.

Enabling {brandname} second level cache provider inside a JPA/Hibernate application that runs in single node is very straightforward.
First, make sure the Hibernate {brandname} cache provider is available in the classpath.
Then, modify the persistence.xml to include these properties:

[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/ispn_second_level_cache.xml[]
----

By default when running standalone, the {brandname} second-level cache provider uses an {brandname} configuration that’s designed for clustered environments.
However, {brandname} also provides a configuration designed for local, single node, environments.
To enable that configuration, set `hibernate.cache.infinispan.cfg` to `org/infinispan/hibernate/cache/commons/builder/infinispan-configs-local.xml` value.
You can find more about the configuration check the link:#default_local_configuration_second_level[Default Local Configuration] section.

A simple tutorial showing how to use {brandname} as Hibernate cache provider in a standalone application can be found
link:https://github.com/infinispan/infinispan-simple-tutorials/tree/main/hibernate-cache/local[here].

=== Single-Node Standalone Spring Application

Using Hibernate within Spring applications is a very common use case.
In this section you will learn what you need to do configure Hibernate within Spring to use {brandname} as second-level cache provider.

As in the previous case, start by making sure that Hibernate {brandname} Cache provider is available in the classpath.
Then, modify `application.properties` file to contain:

    # Use Infinispan second level cache provider
    spring.jpa.properties.hibernate.cache.region.factory_class=infinispan
    #
    # Force using local configuration when only using a single node.
    # Otherwise a clustered configuration is loaded.
    spring.jpa.properties.hibernate.cache.infinispan.cfg=org/infinispan/hibernate/cache/commons/builder/infinispan-configs-local.xml

By default when running standalone, the {brandname} second-level cache provider uses an {brandname} configuration that’s designed for clustered environments.
However, {brandname} also provides a configuration designed for local, single node, environments.
To enable that configuration, set `spring.jpa.properties.hibernate.cache.infinispan.cfg` to `org/infinispan/hibernate/cache/commons/builder/infinispan-configs-local.xml` value.
You can find more about the configuration check the link:#default_local_configuration_second_level[Default Local Configuration] section.

A simple tutorial showing how to use {brandname} as Hibernate cache provider in a Spring application can be found
link:https://github.com/infinispan/infinispan-simple-tutorials/tree/main/hibernate-cache/spring-local[here].

=== Single-Node WildFly Application

In WildFly, {brandname} is the default second level cache provider for JPA/Hibernate.
This means that when using JPA in WildFly, region factory is already set to `infinispan`.
{brandname}'s configuration is located in WildFly's `standalone.xml` file.
It follows the same settings explained in link:#default_local_configuration_second_level[Default Local Configuration] section.

WARNING: When running in WildFly, do not set `hibernate.cache.infinispan.cfg`.
The configuration of the caches comes from WildFly's configuration file.

Several aspects of the {brandname} second level cache provider can be configured directly in `persistence.xml`.
This means that some of those tweaks do not require changing WildFly's `standalone.xml` file.
You can find out more about these changes in the link:#configuration_properties[Configuration Properties] section.

So, to enable Hibernate to use {brandname} as second-level cache, all you need to do is enable second-level cache.
This is explained in detail in the introduction of this chapter.

A simple tutorial showing how to use {brandname} as Hibernate cache provider in a WildFly application can be found
link:https://github.com/infinispan/infinispan-simple-tutorials/tree/main/hibernate-cache/wildfly-local[here].

=== Multi-Node Standalone Hibernate Application

When running a JPA/Hibernate in a multi-node environment and enabling {brandname} second-level cache, it is necessary to cluster the second-level cache so that cache consistency can be guaranteed.
Clustering the {brandname} second-level cache provider is as simple as adding the following property to the `persistence.xml` file:

[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/ispn_second_level_cache_multinode.xml[]
----

The default {brandname} configuration used by the second-level cache provider is already configured to work in a cluster environment, so no need to add any extra properties.
You can find more about the configuration check the link:#default_cluster_configuration_second_level[Default Cluster Configuration] section.

=== Multi-Node Standalone Spring Application

If interested in running a Spring application that uses Hibernate and {brandname} as second level cache, the cache needs to be clustered.
Clustering the {brandname} second-level cache provider is as simple as adding the following property to the `application.properties` file:

    # Use Infinispan second level cache provider
    spring.jpa.properties.hibernate.cache.region.factory_class=infinispan

The default {brandname} configuration used by the second-level cache provider is already configured to work in a cluster environment, so no need to add any extra properties.
You can find more about the configuration check the link:#default_cluster_configuration_second_level[Default Cluster Configuration] section.

=== Multi-Node WildFly Application

As mentioned in the single node WildFly case, {brandname} is the default second level cache provider for JPA/Hibernate when running inside WildFly.
This means that when using JPA in WildFly, region factory is already set to `infinispan`.

When running WildFly multi-node clusters, it is recommended that you start off by using `clustered.xml` configuration file.
Within this file you can find Hibernate {brandname} caches configured with the correct settings to work in a clustered environment.
You can find more about the configuration check the link:#default_cluster_configuration_second_level[Default Cluster Configuration] section.

Several aspects of the {brandname} second level cache provider can be configured directly in `persistence.xml`.
This means that some of those tweaks do not require changing WildFly's `standalone-ha.xml` file.
You can find out more about these changes in the link:#configuration_properties[Configuration Properties] section.

So, to enable Hibernate to use {brandname} as second-level cache, all you need to do is enable second-level cache.
Enabling second-level cache is explained in detail in the introduction of this chapter.

== Configuration Reference

This section is dedicated at explaining configuration in detail as well as some extra configuration options.

[[default_local_configuration_second_level]]
=== Default Local Configuration

{brandname} second-level cache provider comes with a configuration designed for local, single node, environments.
These are the characteristics of such configuration:

Entities, collections, queries and timestamps are stored in non-transactional local caches.

Entities and collections query caches are configured with the following eviction settings:

* Eviction wake up interval is 5 seconds.
* Max number of entries are 10,000.
* Max idle time before expiration is 100 seconds.
* Default eviction algorithm is LRU, least recently used.

You can change these settings on a per entity or collection basis or per individual entity or collection type.
More information in the link:#configuration_properties[Configuration Properties] section below.

_No eviction/expiration is configured for timestamp caches_, nor it's allowed.

[[default_cluster_configuration_second_level]]
=== Default Cluster Configuration

{brandname} second-level cache provider default configuration is designed for multi-node clustered environments.
The aim of this section is to explain the default settings for each of the different global data type caches (entity, collection, query and timestamps), why these were chosen and what are the available alternatives.
These are the characteristics of such configuration:

.Entities and Collections

By default all _entities and collections are configured to use a synchronous invalidation_ as clustering mode.
Whenever a new _entity or collection is read from database_ and needs to be cached, _it's only cached locally_ in order to reduce intra-cluster traffic.
This option can be changed so that entities/collections are cached cluster wide, by switching the entity/collection cache to be replicated or distributed.
How to change this option is explained in the link:#configuration_properties[Configuration Properties] section.

WARNING: When data read from the database is put in the cache, with replicated or distributed caches,
the data is propagated to other nodes using asynchronous communication.
In the presence of concurrent database loads, one operation will succeed while others might fail (silently).
This is fine because they'd all be trying to put the same data loaded from the database.
This has the side effect that under these circumstances, the cache might not be up to date right after making the JPA call that leads to the database load.
However, the cache will eventually contain the data loaded, even if it happens after a short delay.

All _entities and collections are configured to use a synchronous invalidation_ as clustering mode.
This means that when an entity is updated, the updated cache will send a message to the other members of the cluster telling them that the entity has been modified.
Upon receipt of this message, the other nodes will remove this data from their local cache, if it was stored there.
This option can be changed so that both local and remote nodes contain the updates by configuring entities or collections to use a replicated or distributed cache.
With replicated caches all nodes would contain the update, whereas with distributed caches only a subset of the nodes.
How to change this option is explained in the link:#configuration_properties[Configuration Properties] section.

All _entities and collections have initial state transfer disabled_ since there's no need for it.

Entities and collections are configured with the following eviction settings.
You can change these settings on a per entity or collection basis or per individual entity or collection type.
More information in the link:#configuration_properties[Configuration Properties] section below.

* Eviction wake up interval is 5 seconds.
* Max number of entries are 10,000.
* Max idle time before expiration is 100 seconds.
* Default eviction algorithm is LRU, least recently used.

.Queries

Assuming that query caching has been enabled for the persistence unit (see chapter introduction), the query cache is configured so that _queries are only cached locally_.
Alternatively, you can configure query caching to use replication by selecting the `replicated-query` as query cache name.
However, replication for query cache only makes sense if, and only if, all of this conditions are true:

* Performing the query is quite expensive.
* The same query is very likely to be repeatedly executed on different cluster nodes.
* The query is unlikely to be invalidated out of the cache

NOTE: Hibernate must aggressively invalidate query results from the cache any time any instance of one of the entity types targeted by the query.
All such query results are invalidated, even if the change made to the specific entity instance would not have affected the query result.
For example: the cached result of `SELECT id FROM cars where color = 'red'` is thrown away when you call `INSERT INTO cars VALUES ..., color = 'blue'`.
Also, the result of an update within a transaction is not visible to the result obtained from the query cache.

_query cache_ uses the _same eviction/expiration settings as for entities/collections_.

_query cache has initial state transfer disabled_. It is not recommended that this is enabled.

Up to Hibernate 5.2 both transactional and non-transactional query caches have been supported, though non-transactional variant is recommended. Hibernate 5.3 drops support for transactional caches, only non-transactional variant is supported. If the cache is configured with transactions this setting is ignored and warning is logged.

.Timestamps

The _timestamps cache is configured with asynchronous replication_ as clustering mode.
Local or invalidated cluster modes are not allowed, since all cluster nodes must store all timestamps.
As a result, _no eviction/expiration is allowed for timestamp caches either_.

IMPORTANT: Asynchronous replication was selected as default for timestamps cache for performance reasons.
A side effect of this choice is that when an entity/collection is updated, for a very brief period of time stale queries might be returned.
It's important to note that due to how {brandname} deals with asynchronous replication, stale queries might be found even query is done right after an entity/collection update on same node.

NOTE: Hibernate must aggressively invalidate query results from the cache any time any instance of one of the entity types is modified.
All cached query results referencing given entity type are invalidated, even if the change made to the specific entity instance would not have affected the query result.
The timestamps cache plays here an important role - it contains last modification timestamp for each entity type.
After a cached query results is loaded, its timestamp is compared to all timestamps of the entity types that are referenced in the query.
If any of these is higher, the cached query result is discarded and the query is executed against DB.
This requires synchronization of the wall clock across the cluster to work as expected.

[[configuration_properties]]
=== Configuration Properties

As explained above, {brandname} second-level cache provider comes with default configuration in `infinispan-config.xml` that is suited for clustered use.
If there's only single JVM accessing the DB, you can use more performant `infinispan-config-local.xml` by setting the `hibernate.cache.infinispan.cfg` property.
If you require further tuning of the cache, you can provide your own configuration.
Caches that are not specified in the provided configuration will default to `infinispan-config.xml` (if the provided configuration uses clustering) or `infinispan-config-local.xml`.

WARNING: It is not possible to specify the configuration this way in WildFly.
Cache configuration changes in WildFly should be done either modifying the cache configurations inside the application server configuration, or creating new caches with the desired tweaks and plugging them accordingly.
See examples below on how entity/collection specific configurations can be applied.

[[caching_provider_infinispan_config_example]]
.Use custom {brandname} configuration
[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/ispn_caching_provider.xml[]
----

NOTE: If the cache is configured as transactional, {brandname} cache provider automatically sets transaction manager so that the TM used by {brandname} is the same as TM used by Hibernate.

Cache configuration can differ for each type of data stored in the cache.
In order to override the cache configuration template, use property `hibernate.cache.infinispan._data-type_.cfg` where `_data-type_` can be one of:

* `entity`:
Entities indexed by `@Id` or `@EmbeddedId` attribute.
* `immutable-entity`:
Entities tagged with `@Immutable` annotation or set as `mutable=false` in mapping file.
* `naturalid`:
Entities indexed by their `@NaturalId` attribute.
* `collection`:
All collections.
* `timestamps`:
Mapping _entity type_ -> _last modification timestamp_.
Used for query caching.
* `query`:
Mapping _query_ -> _query result_.
* `pending-puts`:
Auxiliary caches for regions using invalidation mode caches.

For specifying cache template for specific region, use region name instead of the `_data-type_`:

[[caching_provider_infinispan_config_cache_example]]
.Use custom cache template
[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/ispn_caching_provider_custom.xml[]
----

.Use custom cache template in WildFly
When applying entity/collection level changes inside JPA applications deployed in WildFly, it is necessary to specify deployment name and persistence unit name (separated by `#` character):

[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/ispn_caching_provider_wfly.xml[]
----

IMPORTANT: Cache configurations are used only as a template for the cache created for given region.
Usually each entity hierarchy or collection has its own region

WARNING: Except for eviction/expiration settings, it is highly recommended not to deviate from the template configuration settings.

Some options in the cache configuration can also be overridden directly through properties.
These are:

* `hibernate.cache.infinispan._something_.eviction.strategy`:
Available options are `NONE`, `LRU` and `LIRS`.
* `hibernate.cache.infinispan._something_.eviction.max_entries`:
Maximum number of entries in the cache.
* `hibernate.cache.infinispan._something_.expiration.lifespan`:
Lifespan of entry from insert into cache (in milliseconds).
* `hibernate.cache.infinispan._something_.expiration.max_idle`:
Lifespan of entry from last read/modification (in milliseconds).
* `hibernate.cache.infinispan._something_.expiration.wake_up_interval`:
Period of thread checking expired entries.
* `hibernate.cache.infinispan.statistics`:
Globally enables/disable {brandname} statistics collection, and their exposure via JMX.

Example:

[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/hibernate_cache_properties.xml[]
----

With the above configuration, you're overriding whatever eviction/expiration settings were defined for the default entity cache name in the {brandname} cache configuration used.
This happens regardless of whether it's the default one or user defined.
More specifically, we're defining the following:

* All entities to use LRU eviction strategy
* The eviction thread to wake up every 2 seconds (2000 milliseconds)
* The maximum number of entities for each entity type to be 5000 entries
* The lifespan of each entity instance to be 1 minute (60000 milliseconds).
* The maximum idle time for each entity instance to be 30 seconds (30000 milliseconds).

You can also override eviction/expiration settings on a per entity/collection type basis.
This allows overrides that only affects a particular entity (i.e. `com.acme.Person`) or collection type (i.e. `com.acme.Person.addresses`).
Example:

[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/hibernate_cache_eviction.xml[]
----

Inside of WildFly, same as with the entity/collection configuration override, eviction/expiration settings would also require deployment name and persistence unit information
(a working example can be found
link:https://github.com/infinispan/infinispan-simple-tutorials/tree/main/hibernate-cache/wildfly-local[here]
):

[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/hibernate_cache_deployment.xml[]
----

== Cache Strategies

{brandname} cache provider supports all Hibernate cache strategies:
`transactional`, `read-write`, `nonstrict-read-write` and `read-only`.

Integrations with Hibernate 4.x required _transactional invalidation caches_ and in integrations with Hibernate &le; 5.2 _transactional invalidation caches_ are supported (in JTA environment). However for all 5.x versions _non-transactional caches_ are preferred. With Hibernate 5.3 the support for transactional caches has been dropped completely, and both `read-write` and `transactional` use the same implementation. {brandname} provides the same consistency guarantees for both `transactional` and `read-write` strategies, use of transactions is considered an implementation detail.

In integrations with Hibernate 5.2 or lower the actual setting of cache concurrency mode (`read-write` vs. `transactional`) is not honored on invalidation caches, the appropriate strategy is selected based on the cache configuration (_non-transactional_ vs. _transactional_).

Support for _replicated/distributed_ caches for `read-write` and `read-only` strategies has been added during 5.x development and this requires exclusively _non-transactional configuration_.
Also eviction should not be used in this configuration as it can lead to consistency issues. Expiration (with reasonably long max-idle times) can be used.

`Nonstrict-read-write` strategy is supported on _non-transactional distributed/replicated_ caches, but the eviction should be turned off as well. In addition to that, the entities must use versioning. This means that this strategy cannot be used for caching natural IDs (which are never versioned). This mode mildly relaxes the consistency - between DB commit and end of transaction commit a stale read may occur in another transaction.
However this strategy uses less RPCs and can be more performant than the other ones.

`Read-only` mode is supported in all configurations mentioned above but use of this mode currently does not bring any performance gains.

The available combinations are summarized in table below:

[[caching_provider_infinispan_compatibility_table]]
.Cache concurrency strategy/cache mode compatibility table
[options="header"]
|===
|Concurrency strategy|Cache transactions         |Cache mode             |Eviction
|transactional       |&le; 5.2 transactional     |invalidation           |yes
|transactional       |&ge; 5.3 non-transactional |invalidation           |yes
|read-write          |non-transactional |invalidation           |yes
|read-write          |non-transactional |distributed/replicated |no
|nonstrict-read-write|non-transactional |distributed/replicated |no
|===

Changing caches to behave different to the default behaviour explained in previous section is explained in the link:#configuration_properties[Configuration Properties] section.

IMPORTANT: Use of transactional caches is possible only in JTA environment. Hibernate supports JDBC-only transactions but {brandname} transactional caches do not integrate with these. Therefore, in non-JTA environment the only option is to use `read-write`, `nonstrict-read-write` or `read-only` on non-transactional cache. Configuring the cache as transactional in non-JTA can lead to undefined behaviour.

[[caching_provider_infinispan_stale_read_example]]
.Stale read with `nonstrict-read-write` strategy
[source,java,indent=0]
----
include::code_examples/StaleReadExample.java[]
----

== Using minimal puts

Hibernate offers a configuration option `hibernate.cache.use_minimal_puts` which is off by default in {brandname} implementation. This option checks if the cache contains given key before updating the value from database (put-from-load) and omits the update if the cached value is already present.
When using invalidation caches it makes sense to keep this off as the put-from-load is local node-only and silently fails if the entry is locked. With replicated/distributed caches the update is applied to remote nodes, even if the local node already contains the entry, and this has higher performance impact, so it might make sense to turn this option on and avoid updating the cache.
