EhCache Implementation Using Spring Boot Framework

EhCache

In this tutorial I am going to explain you how to use EhCache in application with Spring Boot framework. Caching mechanism improves application’s performance by loading the repeated data from the cache instead of fetching it from persistent storage, thus reducing the network round trips.

The terms “buffer” and “cache” tend to be used interchangeably; note however they represent different things.

A buffer is used traditionally as an intermediate temporary store for data between a fast and a slow entity. As one party would have to wait for the other affecting performance, the buffer alleviates this by allowing entire blocks of data to move at once rather then in small chunks. The data is written and read only once from the buffer. Furthermore, the buffers are visible to at least one party which is aware of it.

A cache on the other hand by definition is hidden and neither party is aware that caching occurs. It as well improves performance but does that by allowing the same data to be read multiple times in a fast fashion.

At its core, the abstraction applies caching to Java methods, reducing thus the number of executions based on the information available in the cache. That is, each time a targeted method is invoked, the abstraction will apply a caching behavior checking whether the method has been already executed for the given arguments. If it has, then the cached result is returned without having to execute the actual method; if it has not, then method is executed, the result cached and returned to the user so that, the next time the method is invoked, the cached result is returned. This way, expensive methods (whether CPU or IO bound) can be executed only once for a given set of parameters and the result reused without having to actually execute the method again. The caching logic is applied transparently without any interference to the invoker.

Related Posts:

Prerequisites

Java 1.8+, Spring Boot 2.4.1 – 2.7.1, EhCache 3.9.0+, Maven 3.6.3 – 3.8.5, Gradle 6.7.1

Project Setup

You can create maven or gradle based project in your favorite IDE or tool.

For maven based project use the following pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>

<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>com.roytuts</groupId>
	<artifactId>spring-boot-ehcache-example</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>12</maven.compiler.source>
		<maven.compiler.target>12</maven.compiler.target>
	</properties>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.4.1 - 2.7.1</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
		<dependency>
			<groupId>org.ehcache</groupId>
			<artifactId>ehcache</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

For gradle based project use the following build.gradle script:

buildscript {
	ext {
		springBootVersion = '2.4.1'
	}
	
    repositories {
    	maven {
    		url 'https://plugins.gradle.org/m2/'
    	}
    }
    
    dependencies {
    	classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

plugins {
    id 'java-library'
    id 'org.springframework.boot' version "${springBootVersion}"
}

sourceCompatibility = 12
targetCompatibility = 12

repositories {
    mavenCentral()
    jcenter()
}

dependencies {
	implementation("org.springframework.boot:spring-boot-starter:${springBootVersion}")
	implementation("org.springframework.boot:spring-boot-starter-cache:${springBootVersion}")
	implementation 'org.ehcache:ehcache:3.9.0'
    runtime "org.springframework.boot:spring-boot-devtools:${springBootVersion}"
}

EhCache Config

The ehcache configuration is put into the class path folder – src/main/resources. If this class path folder does not exist then you can create src/main/resources folder for putting the resource files.

The ehcache configuration is put into an XML file called ehcache.xml in the class path folder with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd">

	<!-- default cache configurations if no cache configuration is defined -->
	<defaultCache eternal="true" maxElementsInMemory="100"
		overflowToDisk="false" memoryStoreEvictionPolicy="LFU" />

	<!-- define our own cache configuration -->
	<cache name="employee" maxElementsInMemory="10000"
		eternal="false" overflowToDisk="false" memoryStoreEvictionPolicy="LFU" />
</ehcache>

In the above configuration I have configured both default and custom cache configurations. I have also specified the number of elements to be put into cache memory, the cache eviction policy, whether to save to disk or not when cache memory becomes overflown, also I do not want to persist data in the cache forever.

Application Config

Application configuration is generally done in application.properties/yml/yaml file. As I have configured the ehcache using an XML file so I need to load this file while application starts up and in the application.properties file I am going to load this XML config file. This application.properties file is put into the class path folder src/main/resources.

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

Note: for Spring Boot 2.7.0/2.7.1, you may get the following exception:

Caused by: java.lang.IllegalArgumentException: Cannot find cache named 'employee' for Builder[public java.util.List com.roytuts.spring.boot.ehcache.service.EmployeeService.getListOfEmployees()] caches=[employee] | key='' | keyGenerator='' | cacheManager='' | cacheResolver='' | condition='' | unless='' | sync='false'

To resolve the issue, you need to add the following line (key/value) into the application.properties file:

spring.cache.cache-names=employee

Model Class

As you have seen in the ehcache.xml file that I have created my own cache with the name of employee, so I am going to put employee data into the cache.

So I am creating the following Employee class with three attributes:

public class Employee {

	private int id;
	private String name;
	private String role;

	public Employee() {
	}

	public Employee(int id, String name, String role) {
		this.id = id;
		this.name = name;
		this.role = role;
	}

	// getters and setters

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", role=" + role + "]";
	}

}

Spring Service

I am applying the cache on service layer code. So I am creating a Spring service class where I will implement the ehcache. Actual data should come from a persistence storage or other external services.

@Service
public class EmployeeService {

	@Cacheable(value = "employee")
	public List<Employee> getListOfEmployees() {
		System.out.println("getListOfEmployees is running...");

		List<Employee> employees = new ArrayList<Employee>(4);

		employees.add(new Employee(1000, "Sumit", "Manager"));
		employees.add(new Employee(1001, "Souvik", "Java Developer"));
		employees.add(new Employee(1002, "Liton", "SQl Developer"));
		employees.add(new Employee(1003, "Debina", "Leader"));

		return employees;
	}

	@Cacheable(value = "employee", key = "#name")
	public Employee findEmployeeByName(String name, List<Employee> employees) {
		System.out.println("findEmployeeByName is running...");

		for (Employee emp : employees) {
			if (emp.getName().equalsIgnoreCase(name)) {
				return emp;
			}
		}

		return null;
	}

}

In the above code I have put @Cacheable annotation for caching employee data. Here you see that the value for this annotation is employee which I have configured in the ehcache.xml file.

The method findEmployeeByName() has annotation with key also. This key actually indicates the unique value which is used to find the employee in the cache.

Main Class

A class with main method and @SpringBootApplication annotation is enough to start the Spring Boot application. Here I am starting application in CLI mode using CommandLineRunner interface.

@EnableCaching
@SpringBootApplication
public class SpringEhCacheApp implements CommandLineRunner {

	@Autowired
	private EmployeeService employeeService;

	public static void main(String[] args) {
		SpringApplication.run(SpringEhCacheApp.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		List<Employee> employees = employeeService.getListOfEmployees();

		System.out.println(employees);
		System.out.println("---------------------------------------------------");

		employees = employeeService.getListOfEmployees();

		System.out.println(employees);
		System.out.println("---------------------------------------------------");

		Employee employee = employeeService.findEmployeeByName("Sumit", employees);

		System.out.println(employee);
		System.out.println("---------------------------------------------------");

		employee = employeeService.findEmployeeByName("Sumit", employees);

		System.out.println(employee);
		System.out.println("---------------------------------------------------");

		employee = employeeService.findEmployeeByName("Liton", employees);
		System.out.println(employee);
		System.out.println("---------------------------------------------------");
	}

}

Testing EhCache

Running the above main class will give you the following output:

getListOfEmployees is running...
[Employee [id=1000, name=Sumit, role=Manager], Employee [id=1001, name=Souvik, role=Java Developer], Employee [id=1002, name=Liton, role=SQl Developer], Employee [id=1003, name=Debina, role=Leader]]
---------------------------------------------------
[Employee [id=1000, name=Sumit, role=Manager], Employee [id=1001, name=Souvik, role=Java Developer], Employee [id=1002, name=Liton, role=SQl Developer], Employee [id=1003, name=Debina, role=Leader]]
---------------------------------------------------
findEmployeeByName is running...
Employee [id=1000, name=Sumit, role=Manager]
---------------------------------------------------
Employee [id=1000, name=Sumit, role=Manager]
---------------------------------------------------
findEmployeeByName is running...
Employee [id=1002, name=Liton, role=SQl Developer]
---------------------------------------------------

So, how will you detect whether ehcache was working or not ?

Well, you see in the output that getListOfEmployees is running... and findEmployeeByName is running... are printed inside the methods only once for the same set of parameters and for the subsequent calls to the System.out.println() have not been executed. It means for the first time only methods were invoked but for subsequent calls the values were retrieved from the cache.

Source Code

Download

Leave a Reply

Your email address will not be published. Required fields are marked *