Hibernate 5 - Query cache, entity cache and collection cache example



Hibernate 5 - Query cache, entity cache and collection cache example

Technologies used:   Java SE 1.8 | Hibernate 5.2.10.Final | Ehcache 3.3.1 | JCache API 1.0 | Maven 3.3.9 | MySQL 5.7.12 | Eclipse Neon.3

In our previous post, we have learned how to configure cache in Hibernate application using the JCache with Ehcache’s implementation.

In this post, I will show you -

  • How to use the @org.hibernate.annotations.Cache annotation for cachnig  entities and collection properties.
  • How to use the Query.setCacheable() to enable the result caching of a particular query.

 

Jar Dependencies

Open pom.xml file of your maven project and add the following dependencies in it.

  <dependencies>
    <!-- Mysql Connector -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>6.0.5</version>
    </dependency>

    <!-- Hibernate 5.2.10 Final -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-core</artifactId>
      <version>5.2.10.Final</version>
    </dependency>

    <!-- Hibernate Jcache -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-jcache</artifactId>
      <version>5.2.10.Final</version>
    </dependency>
    
  <!-- Ehcache 3.x -->
    <dependency>
      <groupId>org.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>3.3.1</version>
    </dependency>

    <!--Log4j2 API -->
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>2.8.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>2.8.2</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>2.8.2</version>
    </dependency>
  </dependencies>

 

Hibernate Utility class

To enable entity cache and collection cache, you need to specify the following configuration properties.

1 - Enable second level cache (you can skip this property because default value is true for it).

hibernate.cache.use_second_level_cache = true

2 - Specify cache region factory class.

hibernate.cache.region.factory_class = org.hibernate.cache.jcache.JCacheRegionFactory

3 - Specify cache provider.

hibernate.javax.cache.provider = org.ehcache.jsr107.EhcacheCachingProvider

To enable query cache, you need to specify one more additional configuration property.

hibernate.cache.use_query_cache = true

 

Here is the complete example of HibernateUitl helper class to bootstrap application.

HibernateUtil.java

package com.boraji.tutorial.hibernate;

import java.util.HashMap;
import java.util.Map;

import org.ehcache.jsr107.EhcacheCachingProvider;
import org.hibernate.SessionFactory;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cache.jcache.JCacheRegionFactory;
import org.hibernate.cfg.Environment;

import com.boraji.tutorial.hibernate.entity.Department;
import com.boraji.tutorial.hibernate.entity.Employee;

/**
 * @author imssbora
 */
public class HibernateUtil {

   private static StandardServiceRegistry registry;
   private static SessionFactory sessionFactory;

   public static SessionFactory getSessionFactory() {
      if (sessionFactory == null) {
         try {
            StandardServiceRegistryBuilder registryBuilder = 
                     new StandardServiceRegistryBuilder();

            Map<String, Object> settings = new HashMap<>();
            settings.put(Environment.DRIVER, "com.mysql.cj.jdbc.Driver");
            settings.put(Environment.URL,"jdbc:mysql://localhost:3306/BORAJI?useSSL=false");
            settings.put(Environment.USER, "root");
            settings.put(Environment.PASS, "admin");
            settings.put(Environment.HBM2DDL_AUTO, "update");

            // Enable second level cache (default value is true)
            settings.put(Environment.USE_SECOND_LEVEL_CACHE, true);

            // Enable Query cache
            settings.put(Environment.USE_QUERY_CACHE, true);

            // Specify cache region factory class
            settings.put(Environment.CACHE_REGION_FACTORY, JCacheRegionFactory.class);

            // Specify cache provider
            settings.put("hibernate.javax.cache.provider", EhcacheCachingProvider.class);

            registryBuilder.applySettings(settings);
            registry = registryBuilder.build();
            MetadataSources sources = new MetadataSources(registry)
                  .addAnnotatedClass(Department.class).addAnnotatedClass(Employee.class);
            Metadata metadata = sources.getMetadataBuilder().build();
            sessionFactory = metadata.getSessionFactoryBuilder().build();
         } catch (Exception e) {
            if (registry != null) {
               StandardServiceRegistryBuilder.destroy(registry);
            }
            e.printStackTrace();
         }
      }
      return sessionFactory;
   }

   public static void shutdown() {
      if (registry != null) {
         StandardServiceRegistryBuilder.destroy(registry);
      }
   }
}

 

Entity classes

You can annotate your entity class and collection property with @Cache annotation for cachnig as follows.

Department.java

package com.boraji.tutorial.hibernate.entity;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "DEPARTMENT")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Department {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "DPT_ID")
   private long id;

   @Column(name = "NAME", nullable = false, unique = true)
   private String name;

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "department")
   @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
   private List<Employee> employees = new ArrayList<>();

   //Getter and Setter methods
}

Employee.java

package com.boraji.tutorial.hibernate.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "EMPLOYEE")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Employee {

   @Id
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   @Column(name = "EMP_ID")
   private long id;

   @Column(name = "NAME", nullable = false)
   private String name;

   @Column(name = "DESIGNATION")
   private String designation;

   @ManyToOne
   @JoinColumn(name = "DPT_ID")
   private Department department;

   //Getter and Setter methods
}

 

Hibernate Cache Logging

You can check/log the Hibernate second-level cache activities by enabling the logging.

You need to create a log4j2.xml file under src/main/resources folder and write the following code in it.

log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
  <Appenders>
    <!-- Console Appender -->
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout pattern="%d{yyyy-MMM-dd HH:mm:ss a} [%t] %-5level %logger{36} - %msg%n" />
    </Console>
  </Appenders>
  <Loggers>
    <!-- Log SQL statements -->
    <Logger name="org.hibernate.SQL" level="debug" additivity="false">
      <AppenderRef ref="Console" />
    </Logger>
    <!-- Log cache -->
    <Logger name="org.hibernate.cache" level="debug" additivity="false">
      <AppenderRef ref="Console" />
    </Logger>
    <Logger name="org.ehcache" level="debug" additivity="false">
      <AppenderRef ref="Console" />
    </Logger>
    <Root level="error">
      <AppenderRef ref="Console" />
    </Root>
  </Loggers>
</Configuration>

 

Entity cache and collection cache example

Earlier we have annotated the Employee and Department entities and List<Employee> property with the @Cache annotation. To know whether the @Cache annotation is working or not, create an EntityCollectionCacheExample class and write the following code in it.

EntityCollectionCacheExample.java

package com.boraji.tutorial.hibernate;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.boraji.tutorial.hibernate.entity.Department;
import com.boraji.tutorial.hibernate.entity.Employee;

/**
 * @author imssbora
 */
public class EntityCollectionCacheExample {

   public static void main(String[] args) {
      Session session = null;
      Transaction transaction = null;
      try {

         //Get Department from DATABASE
         session = HibernateUtil.getSessionFactory().openSession();
         transaction = session.getTransaction();
         transaction.begin();
         Department department=session.get(Department.class, 1l);
         System.out.println("Department :"+department.getName());
         List<Employee> employees=department.getEmployees();
         for (Employee employee : employees) {
            System.out.println("\tEmployee Name : "+employee.getName());
         }
         transaction.commit();
         session.close();
         
         //Get Department from Cache
         session = HibernateUtil.getSessionFactory().openSession();
         transaction = session.getTransaction();
         transaction.begin();
         Department department2=session.get(Department.class, 1l);
         System.out.println("Department :"+department2.getName());
         List<Employee> employees2=department2.getEmployees();
         for (Employee employee : employees2) {
            System.out.println("\tEmployee Name : "+employee.getName());
         }
         transaction.commit();
         session.close();
         
      } catch (Exception e) {
         e.printStackTrace();
      } 
      HibernateUtil.shutdown();
   }
}

On execution of the EntityCollectionCacheExample class, the console output will look like as follows.

Hibernate 5 - Query cache, entity cache and collection cache example| BORAJI.COM

 

Query cache example

Aside from caching entities and collections, you need to set query manually as cacheable for result caching of a particular query.

The following example demonstrates how to set a query as cacheable.

QueryCacheExample.java

package com.boraji.tutorial.hibernate;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.boraji.tutorial.hibernate.entity.Employee;

/**
 * @author imssbora
 */
public class QueryCacheExample {

   public static void main(String[] args) {
      Session session = null;
      Transaction transaction = null;
      try {

         //Get employee list from DATABASE
         session = HibernateUtil.getSessionFactory().openSession();
         transaction = session.getTransaction();
         transaction.begin();
         @SuppressWarnings("unchecked")
         List<Employee> employees=session.createQuery("from Employee")
               .setCacheable(true)
               .setCacheRegion("employee.cache")
               .list();
         for (Employee employee : employees) {
            System.out.println("\tEmployee Name : "+employee.getName());
         }
         transaction.commit();
         session.close();
         
         ///Get employee list from Cached result
         session = HibernateUtil.getSessionFactory().openSession();
         transaction = session.getTransaction();
         transaction.begin();
         @SuppressWarnings("unchecked")
         List<Employee> employees2=session.createQuery("from Employee")
               .setCacheable(true)
               .setCacheRegion("employee.cache")
               .list();
         for (Employee employee : employees2) {
            System.out.println("\tEmployee Name : "+employee.getName());
         }
         transaction.commit();
         session.close();
         
      } catch (Exception e) {
         e.printStackTrace();
      } 
      HibernateUtil.shutdown();
   }
}

On execution of the QueryCacheExample class, the console output will look like as follows.

Hibernate 5 - Query cache, entity cache and collection cache example| BORAJI.COM

 

Download Source: