Hibernate/JPA EhCache Configuration Example

In this post, we will see Hibernate/JPA EhCache Configuration Example Using Spring Boot and Oracle. We are going to use Hibernate 5 and Spring Boot 2.x for this tutorial.

Introduction.

EhCache is used for second-level cache to reduce the number of queries in the database and for performance improvement. Hibernate provides inbuilt support for EhCache and Infinispan. Here we will see hibernate second level cache example using EhCache.

First, we will save one record in the database, and then we will retrieve it from EhCache.

Note – EhCache stores the data in In-Memory and Disk.

Maven Dependency.

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
</dependency>

Hibernate EhCache Configuration.

Step 1 – Enable second level cache and configure EhCacheRegionFactory(EhCacheProvider is deprecated). We need to make the below changes in the application.properties file.

spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory

Step 2 – Define ehcache.xml in the resources folder.


<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
monitoring="autodetect" dynamicConfig="true">

<defaultCache eternal="false" timeToLiveSeconds="30"
memoryStoreEvictionPolicy="LRU" statistics="true" maxElementsInMemory="10000"
overflowToDisk="false" />

<cache name="book" maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="60" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU"
statistics="true">
</cache>

</ehcache>

Step 3 – Use @Cacheable and @Cache annotation with the entity.

@Entity
@Cacheable
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region = "book" )
public class Book implements Serializable {

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int bookId;

@Column(name = "book_name")
private String bookName;

}

Hibernate/JPA EhCache Configuration Example Using Spring Boot and Oracle from Scratch(read-only).

Open eclipse and create maven project, Don’t forget to check ‘Create a simple project (skip)’ click on next.  Fill all details(GroupId – hibernateehcache, ArtifactId – hibernateehcache and name – hibernateehcache) and click on finish. Keep packaging as the jar.

Modify pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>hibernateehcache</groupId>
	<artifactId>hibernateehcache</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>hibernateehcache</name>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.1.RELEASE</version>
	</parent>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>

		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>

		<!-- oracle maven dependency -->
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.3</version>
		</dependency>

		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-ehcache</artifactId>
		</dependency>

	</dependencies>
</project>
  

If you see any error for oracle dependency then follow these steps.


Directory structure –

Hibernate/JPA EhCache Configuration Example

Student.java

package com.netsurfingzone.entity;

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 org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Cacheable
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region = "book" )
public class Book implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int bookId;

	@Column(name = "book_name")
	private String bookName;

	public int getBookId() {
		return bookId;
	}

	public void setBookId(int bookId) {
		this.bookId = bookId;
	}

	public String getBookName() {
		return bookName;
	}

	public void setBookName(String bookName) {
		this.bookName = bookName;
	}

}

StudentController.java

package com.netsurfingzone.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.netsurfingzone.entity.Book;
import com.netsurfingzone.service.BookService;


@RestController
@RequestMapping(value = "/book")
public class BookController {

	@Autowired
	private BookService bookService;

	@RequestMapping(value = "/savebook", method = RequestMethod.POST)
	@ResponseBody
	public Book saveBook(@RequestBody Book book) {

		Book bookResponse = bookService.saveBook(book);
		return bookResponse;
	}

	@RequestMapping(value = "/{bookId}", method = RequestMethod.GET)
	@ResponseBody
	public Book getBookDetails(@PathVariable int bookId) {
		Book bookResponse = bookService.findById(bookId);

		return bookResponse;
	}

}

Note – See more details about @Controller and RestController here.

StudentRepository.java – interface

package com.netsurfingzone.repository;

import java.io.Serializable;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.netsurfingzone.entity.Book;

@Repository
public interface BookRepository extends CrudRepository<Book,Serializable> {
	
}

StudentService.java – interface

package com.netsurfingzone.service;

import org.springframework.stereotype.Component;
import com.netsurfingzone.entity.Book;

@Component
public interface BookService {
	public Book saveBook(Book book);
	public Book findById(int bookId);
}

Note – See here more about @Component, @Controller, @Service and @Repository annotations here.

StudentServiceImpl.java

package com.netsurfingzone.serviceimpl;

import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.netsurfingzone.entity.Book;
import com.netsurfingzone.repository.BookRepository;
import com.netsurfingzone.service.BookService;

@Service("bookServiceImpl")
public class BookServiceImpl implements BookService {

	@Autowired
	private BookRepository bookRepository;

	@Transactional
	@Override
	public Book saveBook(Book book) {
		book = bookRepository.save(book);
		return book;
	}

	@Transactional
	public Book findById(int bookId) {
		Optional<Book> bookResponse = bookRepository.findById(bookId);
		return bookResponse.get();
	}

}

SpringMain.java

package com.netsurfingzone.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages="com.netsurfingzone.*")
@EntityScan("com.netsurfingzone.*")
public class SpringMain {
	public static void main(String[] args) {

        SpringApplication.run(SpringMain.class, args);
    }

}

Note – See more details about @ComponentScan here.

JpaConfig.java

package com.netsurfingzone.config;

import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration
@EnableJpaRepositories(basePackages = "com.netsurfingzone.repository")
@EnableCaching
public class JpaConfig {

}

Note – See more details about @Configuration annotations here.

application.properties

# Connection url for the database
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE
spring.datasource.username=SYSTEM
spring.datasource.password=oracle
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
# Show or not log for each sql query
spring.jpa.show-sql = true
spring.jpa.properties.hibernate.format_sql=true
 
spring.jpa.hibernate.ddl-auto =create
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.Oracle10gDialect
 
server.port = 9091

#show sql values
logging.level.org.hibernate.type.descriptor.sql=trace
#spring.jpa.hibernate.logging.level.sql =FINE
#show sql statement
#logging.level.org.hibernate.SQL=debug


spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.use_query_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory

#spring.cache.type=ehcache
#spring.cache.ehcache.config=classpath:ehcache.xml

The spring.jpa.properties.hibernate.cache.use_second_level_cache=true is used to enabled second level cache.

The spring.jpa.properties.hibernate.cache.use_query_cache=true is used to enable Query Cache.

spring.jpa.properties.hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory is EhCache provider.

ehcache.xml


<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true"
	monitoring="autodetect" dynamicConfig="true">

	<defaultCache eternal="false" timeToLiveSeconds="30"
		memoryStoreEvictionPolicy="LRU" statistics="true" maxElementsInMemory="10000"
		overflowToDisk="false" />

	<cache name="book" maxEntriesLocalHeap="10000" eternal="false"
		timeToIdleSeconds="60" timeToLiveSeconds="60" memoryStoreEvictionPolicy="LRU"
		statistics="true">
	</cache>

</ehcache>

Testing of example using postman.

Let’s run the SpringMain class(run as java application).

Perform save operation first using below REST API.

http://localhost:9091/book/savebook
{
    "bookName": "Alchemist"
}
 

Response Data.

Now perform get operation.

GET-  http://localhost:9091/book/1

Since we have defined time 60 seconds in ehcache.xml for the book, EhCache will store data in In-Memory for 60 seconds. For testing, hit the get URL(https://localhost:9091/book/1) multiple times from the postman and observe the console(since we have defined spring.jpa.show-sql = true in application.properties file query will print at the console) hibernate will not fire select query.

After 60 seconds if you again perform get operation, hibernate will fire the select query and for the next 60 seconds, you can retrieve data from the EhCache.

Note – Change the value of spring.jpa.properties.hibernate.cache.use_second_level_cache=false and restart the application and perform save operation. Now perform retrieve operation multiple times and observe the console. Hibernate will fire the query for each request since we have disabled the second level cache.

Hibernate/JPA EhCache example for Collection Types( OneToMany association).

In this section, we will see how to implement Hibernate/JPA EhCache for OneToMany association mapping. Suppose we have two entities Book and Story which are OneToMany relationship(one book can have many stories).

In this case below EhCache configuration and code change needed.

Define new cache for child(Story) entity in ehcache.xml.

<cache name="story" maxEntriesLocalHeap="10000" eternal="false"
timeToIdleSeconds="20" timeToLiveSeconds="20"
memoryStoreEvictionPolicy="LRU" statistics="true">
</cache>

Entity level changes.

We need to define @Cache annotation with the child field(storyList).

@OneToMany(cascade = CascadeType.ALL, fetch= FetchType.LAZY)
@JoinColumn(name = "book_id",referencedColumnName="bookId")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region = "story" )
private List<Story> storyList = new ArrayList<>();

We need to use @Cacheable and @Cache annotation with the child entity(Story.java).

@Entity
@Cacheable
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Story {

}

Book.java

@Entity
@Cacheable
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region = "book" )
public class Book implements Serializable {

	private static final long serialVersionUID = 1L;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int bookId;

	@Column(name = "book_name")
	private String bookName;
	
	@OneToMany(cascade = CascadeType.ALL, fetch= FetchType.LAZY)
	@JoinColumn(name = "book_id",referencedColumnName="bookId")
	@Cache(usage=CacheConcurrencyStrategy.READ_ONLY, region = "story" )
	private List<Story> storyList = new ArrayList<>();
	
	//getter & setter
}

Story.java

@Entity
@Cacheable
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Story {

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private int storyId;

	@Column(name = "story_name")
	private String storyName;

}

Note – The rest of the code would be the same.

HQL EhCache example(Enabling query cache).

We need to add spring.jpa.properties.hibernate.cache.use_query_cache=true in application.properties.

Defining the HQL method to retrieve the data.

@Transactional
public Book getBookUsingHQL(int bookId) {
String hql = "FROM Book b WHERE b.bookId = :bookId";
Query query = entityManager.createQuery(hql, Book.class);
query.setParameter("bookId", bookId);
query.setHint( "org.hibernate.cacheable", "true");
List<Book> books = query.getResultList();
return books.get(0);
}

Cache concurrency strategy in Hibernate.

There are four cache concurrency strategies supported by the hibernate.

  • Read-Only
  • Read-Write
  • Nonstrict-Read-Write
  • Transactional

 Read-Only cache concurrency strategy.

We have already seen CacheConcurrencyStrategy.READ_ONLY example. As the name suggests, this cache concurrency strategy is useful when we have to perform read-only operation. In this cache strategy if we will try to perform the update operation we might get java.lang.UnsupportedOperationException: Can't update readonly object.

@Entity
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "book")
public class Book implements Serializable {

}

The read-only strategy is useful for search/retrieval operation.

Read-Write cache concurrency strategy.

Using CacheConcurrencyStrategy.READ_WRITE we can perform update operation too.

@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Book implements Serializable {

}

Nonstrict-Read-Write Cache concurrency strategy.

Similar to read-write strategy but there might be occasional stale reads upon concurrent access to an entity.

@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Book implements Serializable {

}

Transactional cache concurrency strategy.

Provides serializable transaction isolation level.

@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class Book implements Serializable {

}

That’s all about Hibernate/JPA EhCache Configuration Example Using Spring Boot and Oracle.

You may like.

Spring Data JPA examples.

Hibernate association mapping using Spring Boot.

Hibernate EhCache documentation.

Summary – We have seen about Hibernate/JPA EhCache Configuration Example using Spring boot and Oracle.