Spring Boot EhCache with Example

Spring Boot EhCache with Examples

In this article, I am going to discuss Spring Boot EhCache with Examples. Please read our previous article where we discussed Spring Boot Cache Providers with Examples.

What is EhCache?

As discussed in the previous article, EhCache is a cache provider which is used to manage caches in Spring Boot applications.

What are the advantages of using EhCache?
  • It is fast, lightweight, scalable, and flexible.
  • It allows for both serializable and object performance.
  • It offers cache eviction policies such as LRU, LFU, and FIFO.
  • The cache can be stored in both memory and disk (SSD).
  • It uses SLF4J for logging and has a full implementation of JSR-107 and Jcache.
  • It supports distributed caching via JGroups or JMS and RMI
  • It uses a fluent query language for distributed search.

This makes it a powerful tool for managing data efficiently.

What usage patterns does EhCache have?
  • Cache-aside
  • Cache-as-SoR (system-of-record)
  • Read-through
  • Write-through
  • Write-behind
Cache-Aside

In the cache-aside pattern, the application first checks the cache for data. If the data is present, it is returned directly. If not, the data is fetched from the System of Record (SoR), stored in the cache, and then returned to the application.

Read-Through

The read-through pattern is similar to the cache-aside pattern when it comes to reading data from the cache. The main difference between the two is that the read-through pattern implements the CacheEntryFactory interface, which instructs the cache on how to read an object. When using the read-through pattern, it is recommended to wrap the EhCache instance with a SelfPopulatingCache instance.

Write-Through

The write-through pattern is similar to the cache-aside pattern when it comes to writing data to the cache. The main difference between the two is that the write-through pattern implements the CacheWriter interface. This allows the cache to be configured for both write-through and write-behind patterns. Data is written to the System of Record (SoR) in the same thread of execution.

Write-Behind

The write-behind pattern differs from the other three patterns. It updates cache entries after a specified delay and queues data to be written at a later time in the same thread of execution. Data written using the write-behind pattern occurs outside the scope of the transaction, meaning that a new transaction is created to commit the data to the System of Record (SoR) separate from the main transaction.

Cache-as-SoR (system-of-record)

The cache-as-SoR pattern represents System of Record (SoR) read and write operations in the cache. This reduces the responsibility of the application and uses a combination of read and write patterns, including read-through, write-through, and write-behind. This simplifies the application and allows the cache to solve the thundering-herd problem.

EhCaching Storage Tiers

EhCache allows for the use of various data storage areas, including heap, disk, and clusters. A multi-storage cache can be configured to use more than one storage area and can be arranged and managed in tiers.

The tiers are organized in order, with the bottom-most tier being the authority tier and the other tiers being caching tiers, also known as near or nearer caches. The caching tier can have multiple storage areas and holds the hottest data because it is faster than the authority tier. Other data is stored in the authority tier, which is slower but richer in comparison to the caching tier.

There are four types of data storage supported by EhCache:

  • On-Heap Store
  • Off-Heap Store
  • Disk Store
  • Clustered Store
On-Heap Store

Cache entries are stored in Java heap memory, which is shared with the Java application. This type of storage is fast because it uses heap memory, but space is limited. Additionally, Java’s garbage collector scans the on-heap store for data that can be cleared.

Off-Heap Store

Cache entries are stored in primary memory (RAM) and are not part of the application memory, so they are not scanned by Java’s garbage collector. This type of storage is slower than on-heap storage because cache entries must be moved to the on-heap store before use. It is also limited in size.

Disk Store

Cache entries are stored on a disk, which is much slower than RAM-based stores (both on and off-heap). It is recommended to use a dedicated disk when using a disk store pattern to improve throughput.

Clustered Store

Cache entries are stored on a remote server, which is slower than off-heap storage. A failover server may be used to provide high availability.

EhCaching Storage Tiers

The above diagram describes that:

  • An application can have multiple Cache Managers, each of which can manage multiple caches.
  • These caches can use more than one tier to store cache entries.
  • EhCache stores recently or frequently used data in the faster caching tier.
Spring Boot EhCaching Example

We learned about the advantages and structure of EhCaching. Now, let us learn to implement it using Spring Boot.

Step 1: Create a new project using Spring Initializr in VS Code. Include the following dependencies:

  • Spring Web
  • Spring Cache Abstraction
  • EhCache
  • Cache API

Spring Boot EhCache with Examples

Ensure the dependencies are installed:

Spring Boot EhCache with Examples

Only these two dependencies can be added using the “Choose Dependencies” menu of Spring Initializr. The rest need to be added manually in the pom.xml file. To do this, modify pom.xml to add the following lines:

Spring Boot EhCache with Examples

Step 2: Modify the application.properties file located in the src/main/resources directory:

Spring Boot EhCache with Examples

This line tells Spring Boot to use the configuration file called ehcache.xml.

Step 3: Modify the EhcacheApplication.java file located in the src/main/java/com/dotnet/ehcache directory:

Spring Boot EhCache

We have performed the following modifications:

  • Imported the required packages
  • Added the @EnableCaching annotation to the class.

Step 4: Create a class called Customer.java in the src/main/java/com/dotnet/ehcache directory. This class is a POJO class. You may also use the Customer.java file written in “Spring Boot Caching Example”. Remember to modify the package name if you decide to copy the file. The file should contain the following lines:

Spring Boot EhCache

Step 5: Create a class called CustomerController.java in the src/main/java/com/dotnet/ehcache directory.

Step 6: Modify the newly created CustomerController.java as follows:

Spring Boot EhCache

We have done the following changes:

  • Imported the required packages.
  • Added the @Service annotation to the class. This marks the class as a service class.
  • Created a new hashmap that contains customer data.
  • Populated the hashmap with some sample data.
  • Added a function to send customer information based on customer number.
  • Marked this function as cacheable. This means that the output of this function will be cached.

Step 7: Create a class called ehcache.xml in the src/main/resources directory.

Spring Boot EhCaching

Note that on line 11, the cache name is set to the cache name from line 22 of CustomerController.java. This is because ehcache.xml is a configuration file, which means that ehcache.xml is responsible for configuring each cache (which needs to be handled by ehcache).

Step 8: Modify the EhcacheApplication.java file located in the src/main/java/com/dotnet/ehcache directory:

Spring Boot EhCaching

We have performed the following modifications:

  • Imported some more required packages
  • Added the @Configuration annotation to the class.
  • Added a variable to call the function from CustomerController.java
  • Autowired this variable
  • Created a function to execute some statements. These statements will call the Cacheable function from CustomerController.java.
  • Set this function as a Bean.

Step 9: Compile and execute the application. Ensure compilation is successful:

Spring Boot EhCaching

Congratulations! You now know how to implement EhCache in Spring Boot.

The Complete Example Code
src/main/java/com/dotnet/ehcache/Customer.java
package com.dotnet.ehcache;

public class Customer
{
    private int acNo;
    private String name;
    private String acType;
    private double bal;

    public int getAcNo()                    {return acNo;}
    public void setAcNo(int acNo)           {this.acNo = acNo;}
    public String getName()                 {return name;}
    public void setName(String name)        {this.name = name;}
    public String getAcType()               {return acType;}
    public void setAcType(String acType)    {this.acType = acType;}
    public double getBal()                  {return bal;}
    public void setBal(double bal)          {this.bal = bal;}
    
    public Customer(int acNo, String name, String acType, double bal) {
        this.acNo = acNo;
        this.name = name;
        this.acType = acType;
        this.bal = bal;
    }
    public Customer() {
    }    
}
src/main/java/com/dotnet/ehcache/CustomerController.java
package com.dotnet.ehcache;

import java.util.HashMap;

import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

@Service
public class CustomerController
{
    static HashMap<Integer,Customer> customers = new HashMap<>();
    
    static
    {
        customers.put(1, new Customer(1, "Customer 1", "Savings", 10000.00));
        customers.put(1, new Customer(2, "Customer 2", "Current", 2000000.00));
        customers.put(1, new Customer(3, "Customer 3", "Loan", -2000000.00));
        customers.put(1, new Customer(4, "Customer 4", "Overdraft", -20000.00));
        customers.put(1, new Customer(5, "Customer 5", "Credit Card", -10000.00));
    }

    @Cacheable(cacheNames = "example", key = "#custNo")
    public Customer getCustomerByCustNo (int custNo)
    {
        System.out.println("Fetching student data from cache :-)");
        return customers.get(custNo);
    }
}
src/main/java/com/dotnet/ehcache/EhcacheApplication.java
package com.dotnet.ehcache;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

import org.springframework.context.ApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@SpringBootApplication
@EnableCaching
public class EhcacheApplication
{
 @Autowired
 private CustomerController customerController;

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

 @Bean
 public CommandLineRunner commandLineRunner(ApplicationContext ctx)
 {
  return args -> 
  {
   //This will read from the database
   System.out.println(customerController.getCustomerByCustNo(1));
   //This will read from the cache
   System.out.println(customerController.getCustomerByCustNo(2));
  };
 }
}
src/main/resources/application.properties

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

src/main/resources/ehcache.xml
<config  
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'  
xmlns='http://www.ehcache.org/v3'  
xmlns:jsr107='http://www.ehcache.org/v3/jsr107'>  
<ehcache>  
<diskStore path="java.io.tmpdir" />  
<defaultCache maxElementsInMemory="2000"   
            eternal="true"  
            overflowToDisk="false"   
            timeToLiveSeconds="1200" />  
    <cache name="example"   
            maxElementsInMemory="2000"  
            eternal="false"   
            overflowToDisk="false"   
            timeToLiveSeconds="10000" />  
</ehcache>  
</config>
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.0.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.dotnet</groupId>
    <artifactId>ehcache</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ehcache</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>3.8.1</version>
        </dependency>
        <dependency>
            <groupId>javax.cache</groupId>
            <artifactId>cache-api</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

</project>

In the next article, I am going to discuss Spring Boot Auto Configuration and Dispatcher Servlet with Examples. Here, in this article, I try to explain Spring Boot EhCaching with Examples. I hope you enjoy this Spring Boot EhCaching article.

Leave a Reply

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