Caching with Spring Hibernate - EHCache Basic
Caching is basic concept in ORM which keeps a representation of current database state close to the application, either in memory or on disk of the application server machine.
Cache is a local copy of the data which sits between application and the database. Caching mechanism operates at different levels and types. In general, there are three main types of cache.
- Transaction scope cache : First-level cache is at the transaction level or the unit of work. It’s valid and used as long as the unit of work runs. It corresponds to one session in Hibernate for a single request and is by default enabled for the Hibernate session.
- Process scope cache : This type of cache is shared between many units of work or transactions.
- Cluster scope cache : Shared between multiple processes on the same machine or between multiple machines in a cluster.
Hibernate has a two-level cache architecture:
- First-level cache : This is persistence context cache. This is at the unit-of-work level. It corresponds to one session in Hibernate for a single request and is by default enabled for the Hibernate session.
- Second-level cache : This is either at the process scope or the cluster scope. This is the cache of the state of the persistence entities. At the second-level cache, all persistence contexts that have been started from the same SessionFactory share the same cached data.
In this post, we are going to implement a simple second level caching application to demonstrate the configuration of second level caching.
There are different policies which can be configured for second level caching.
- Whether the second-level cache is enabled
- The Hibernate concurrency strategy
- Cache expiration policies
- The physical format of the cache (memory, indexed files, or cluster-replicated)
We are going to configure second-level cache at process scope using hibernate JPA and ehCache cache provider.
As usual we will create a simple Employee entity and demonstrate caching mechanism. We have annotated our entity with @Cacheable annotation which makes entity eligible for caching. JPA2 provides @Cacheable annotation to instruct persistence provider to decide which entity to be cached.
Employee.java
After setting the entity as cacheable, we need to provide an appropriate caching mechanism to be used by the persistence provider in persistence.xml. <shared-cache-mode> XML element is used to configure cache mode for the entire persistence unit. ENABLE_SELECTIVE indicates that caching is enabled for all entities specified as @Cacheable(true).
Below xml elements to configure Hibernate for second level caching and second level cache provider.
So our final persistence.xml will look like this
persistence.xml
As usual, we will have a spring data repository as DAO layer . @QueryHint is used to indicate persistence provider that we want to cache the query result.
EmployeeRepository.java
- The Hibernate concurrency strategy
- Cache expiration policies
- The physical format of the cache (memory, indexed files, or cluster-replicated)
We are going to configure second-level cache at process scope using hibernate JPA and ehCache cache provider.
As usual we will create a simple Employee entity and demonstrate caching mechanism. We have annotated our entity with @Cacheable annotation which makes entity eligible for caching. JPA2 provides @Cacheable annotation to instruct persistence provider to decide which entity to be cached.
Employee.java
package com.sarf.domain; import java.io.Serializable; import javax.persistence.Cacheable; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Cacheable @Entity @Table(name = "employee") public class Employee implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "employeeId") private int employeeId; @Column(name = "employeeName") private String employeeName; //Setter and getter for fields }
After setting the entity as cacheable, we need to provide an appropriate caching mechanism to be used by the persistence provider in persistence.xml. <shared-cache-mode> XML element is used to configure cache mode for the entire persistence unit. ENABLE_SELECTIVE indicates that caching is enabled for all entities specified as @Cacheable(true).
Below xml elements to configure Hibernate for second level caching and second level cache provider.
So our final persistence.xml will look like this
persistence.xml
As usual, we will have a spring data repository as DAO layer . @QueryHint is used to indicate persistence provider that we want to cache the query result.
EmployeeRepository.java
package com.sarf.repository; import java.util.List; import javax.persistence.QueryHint; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.QueryHints; import org.springframework.stereotype.Repository; import com.sarf.domain.Employee; @Repository() public interface EmployeeRepository extends JpaRepository<Employee, Integer> { @Query(value="select e from Employee as e ") @QueryHints({ @QueryHint(name = "org.hibernate.cacheable", value ="true") }) List<Employee> findAllEmployees(); }Our service layer will have two methods, namely getEmployees() and updateEmployee()
SarfService.java
package com.sarf.service; import java.util.List; import com.sarf.domain.Employee; public interface SarfService { public ListSarfServiceImpl.javagetEmployees(); public void updateEmployee(); }
package com.sarf.service; import java.util.List; import java.util.UUID; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import com.sarf.domain.Employee; import com.sarf.repository.EmployeeRepository; @Service("sarfService") @Transactional public class SarfServiceImpl{ static final Logger logger = Logger.getLogger(SarfServiceImpl.class); @Autowired private EmployeeRepository employeeRepository; public List<Employee> getEmployees() { logger.info("Fetching all employees"); List<Employee> employeeList = employeeRepository.findAllEmployees(); logger.info("Fetched all employees successfully"); return employeeList; } public void updateEmployee() { Employee employee = this.employeeRepository.findOne(1000); employee.setEmployeeName(UUID.randomUUID().toString()); this.employeeRepository.save(employee); logger.info("Employee with id 1000 updated successfully"); } }