Hibernate is able to cache entities or queries using two distinct query mechanism. The first level cache and the second level cache. In this tutorial we will shortly recap the two kind of caches and the configuration needed to get it running with WildFly and older JBoss AS using either JPA 1 and JPA 2 annotations.

Hibernate first level cache

As we said the first level cache is associated with the current session and is used to reduce the number of SQL statements within the same transaction. It's on by default. Let's see an example :

 Session session = getSessionFactory().openSession();
 Transaction tx = session.beginTransaction();
 User user1 = (User) session.load(User.class, 1L);
 System.out.println(user1.getName());
 User user2 = (User) session.load(User.class, 1L);   
 System.out.println(user2.getName());       
 tx.commit();
 session.close();

Here, we are issuing two session.load to retrieve the User object using its primary key. Here only the first query hits the database. his can be verified by adding the following property to your persistence.xml, which displays the sql which is sent to the DB:

<property name="hibernate.show_sql" value="true" />

Note: The System.out.println calls by the way are required to force Hibernate to load data at all. If we would not put them in, nothing would get loaded. This is because data is always by default loaded lazyly in Hibernate.

Hibernate second level cache

The second level cache is used across sessions, which also differentiates it from the session cache, which only – as the name says – has session scope. Hibernate provides a flexible concept to exchange cache providers for the second-level cache. One difference with the first level cache is that it's not on by default but it needs some configuration in your persistence.xml to tell Hibernate to turn on the cache. The configuration differs depending on the version of the application server. Let's see them in detail:

Using second level cache with WildFly

WildFly uses Infinispan as caching provider. Here's the suggested configuration. Here is an example persistence.xml for WildFly:

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
        http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="primary">
        <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source>
        <shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>
        <properties>
            <property name="hibernate.cache.use_second_level_cache" value="true" />
            <property name="hibernate.cache.use_query_cache" value="true" />

            <property name="hibernate.show_sql" value="true" />
            <property name="hibernate.format_sql" value="true"/>
            <property name="hibernate.generate_statistics" value="true" />
        </properties>
    </persistence-unit>
</persistence>

Notice the shared-cache-mode element which is used to determine if the entity needs to be cached or not. The shared-cache-mode element has four possible values:

» ALL: Causes all entities and entity related state and data to be cached

» NONE: Causes caching to be disabled for the persistence unit and all caching is turned off for all entities

» ENABLE_SELECTIVE: Enables the cache and causes entities for which Cacheable(true) is specified to be cached

» DISABLE_SELECTIVE: Enables the cache and causes all entities to be cached except those for which @Cacheable(false) is specified

Therefore, if you use ENABLE_SELECTIVE as shared-cache-mode, only Entities configured as @Cacheable will be eligible to be stored in the Second Level Cache:

@Entity
@Cacheable
public class Customer {

}

The other parameter, namely hibernate.cache.use_query_cache, can be used to activate the Query cache. The query cache can be used whenever you need to access your data in a more flexible way other than the primary key.

The following example shows how you can activate the Query cache on an Entity, by setting the org.hibernate.cacheable to true:

@Entity
@Cacheable
@NamedQueries({
@NamedQuery(
name = "findAll",
query = "FROM Customer c",
hints = { @QueryHint(name = "org.hibernate.cacheable", value =
"true") }
)
})
public class Customer {

}

Monitoring the Second Level Cache

Using the Second Level cache can easily lead to extensive memory usage therefore it’s fundamental to check if it’s actually helpful for the performance of your application. You can monitor the performance of your second level cache at deployment level or at Entity level.

The example below shows how to gather 2nd level cache metrics for the Web application demojpa.war:

/deployment=demojpa.war/subsystem=jpa/hibernate-persistence-unit=unit01#primary:read-resource(include-runtime=true, recursive=true)
{
    "outcome" => "success",
    "result" => {
        "close-statement-count" => 0L,
        "collection-fetch-count" => 1L,
        "collection-load-count" => 1L,
        "collection-recreated-count" => 27L,
        "collection-remove-count" => 1L,
        "collection-update-count" => 0L,
        "completed-transaction-count" => 151L,
        "connect-count" => 312L,
        "enabled" => true,
        "entity-delete-count" => 3L,
        "entity-fetch-count" => 4L,
        "entity-insert-count" => 58L,
        "entity-load-count" => 215L,
        "entity-update-count" => 4L,
        "flush-count" => 88L,
        "hibernate-persistence-unit" => "unit01#primary",
        "optimistic-failure-count" => 0L,
        "prepared-statement-count" => 294L,
        "query-cache-hit-count" => 0L,
        "query-cache-miss-count" => 0L,
        "query-cache-put-count" => 0L,
        "query-execution-count" => 277L,
        "query-execution-max-time" => 6L,
        "query-execution-max-time-query-string" => "select customername0 from Customer as cname0 order by customerCountry1 desc",
        "second-level-cache-hit-count" => 112L,
        "second-level-cache-miss-count" => 4L,
        "second-level-cache-put-count" => 66L,
        "session-close-count" => 245L,
        "session-open-count" => 245L,
        "statistics-enabled" => true,
        "successful-transaction-count" => 245L,
        ...

The second-level-hit-count and query-cache-hit-count attributes need to be compared with their counterpart metrics, that is second-level-miss-count and query-cache-miss-count. The bigger is the ratio between those factors (hit/miss), the better is performing your second level cache and query cache.

For the sake of completeness, we will mention that it is also possible to check the single Entity’s metrics as follows:

/deployment=demojpa.war/subsystem=jpa/hibernate-persistence-unit=unit01#primary/entity-cache=com.demo.Customer:read-resource(include-runtime=true, recursive=true)
{
    "outcome" => "success",
    "result" => {
        "entity-cache-region-name" => "entity-cache-region-name",
        "second-level-cache-count-in-memory" => 15L,
        "second-level-cache-hit-count" => 41L,
        "second-level-cache-miss-count" => 1L,
        "second-level-cache-put-count" => 22L,
        "second-level-cache-size-in-memory" => -1L
    }
}

What is the relation between the 2LC and the Query Cache ?

If you execute again a Query which is able to Cache results, then no SQL statement is sent to the database. Instead the query results are fetched from the Query Cache, and then the cached Entity identifiers are used to access the second level cache.

If you execute again a find by a given Id, the Entity Cache re-hydrates the Entity and returns it. If the second level cache does not contain the results for that particular Id, then an SQL query is issued to load the entity from the database.

Some other useful properties can be used to test if the Second Level Cache is working correctly. In particular:

  • hibernate.show_sql: Will display the queries which are executed against the Database. If you are hitting the second level cache you won't see any SQL in the server's log.
  • hibernate.format_sql: This property, when set to true, will format the SQL statement executed against the Database
  • hibernate.generate_statistics: This property, when set to true, will display Cache statistics after each Statement.

Here you can find a full example of a Java EE application which uses the Second Level Cache and the Query Cache to store Entities: https://github.com/fmarchioni/mastertheboss/tree/master/javaee/2lcache

Using second level cache with JBoss AS 6

JBoss AS 6 uses Infinispan 4 as cache provider. The required properties for persistence.xml follows here:

 <properties>
    <property name="hibernate.cache.use_second_level_cache">true</property>
    <property name="hibernate.cache.use_query_cache">true</property>
    <property name="hibernate.cache.region.factory_class">org.hibernate.cache.infinispan.JndiInfinispanRegionFactory</property>
    <property name="hibernate.cache.infinispan.cachemanager">java:CacheManager/entity</property>
 </properties>
    

The relevant part of this configuration is the cache manager which is bound in the JNDI tree under the name java:CacheManager/entity
The JNDI name to which the hibernate cache container is bound is defined in infinispan-configs.xml:

<infinispan-config name="hibernate" jndi-name="java:CacheManager/entity">
 <infinispan-config xmlns="urn:infinispan:config:4.2">
 ...
 </infinispan-config>
</persistence>

Using second level cache with JBoss AS 5

JBoss AS 5 uses JBoss Cache as caching provider. This is the configuration required to turn on the 2nd level cache on JBoss 5:

<properties>

   <property name="hibernate.cache.use_second_level_cache" value="true"/>
   <property name="hibernate.cache.use_query_cache" value="true"/>

   <property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jbc2.JndiMultiplexedJBossCacheRegionFactory"/>
   <property name="hibernate.cache.region.jbc2.cachefactory" value="java:CacheManager"/>
   <property name="hibernate.cache.region.jbc2.cfg.entity" value="mvcc-entity"/>
   <property name="hibernate.cache.region.jbc2.cfg.collection" value="mvcc-entity"/>
</properties>

By setting the property hibernate.cache.use_second_level_cache to true we are turning on the second-level cache mechanism. The cache, by default, is activated only for entities, so we also need to explicitly set hibernate.cache.use_query_cache to true if we want to cache queries as well.

The second-level cache can be implemented using several different schemas—open source and commercial. In the next property, hibernate.cache.region.factory_class, we are telling Hibernate to use JBoss Cache as the second-level cache implementation.

The next parameter, hibernate.cache.region.jbc2.cachefactory, is specific to the JBoss Cache implementation. It specifies the JNDI name under which the CacheManager to be used is bound. There is no default value, thus the user must specify the property.

The hibernate.cache.region.jbc2.cfg.collection property is also specific to JBoss Cache and details the name of the configuration that should be used for collection caches (in our configuration, mvcc-entity).

0
0
0
s2smodern