Database Caching with Spring Boot and Hazelcast
Tags: databasecaching, springboot, java, beginners
Published: 2021-06-17 13:53:15

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lw1lfz8mxrb360xx80pf.png)
 
 
Signed in as @priyankanandula

<span>After few days _gap_, I am _back_ :sunglasses:</span>

Good day :smiley:, guys! I come back with an important concept during the execution of _database_ operations using  `ORM` tools in Spring Boot.

In this `blog`, we'll talk about **caching** and how to implement it using `Hazel Cast`, one of the _cache providers_.



######Final Project Structure
![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10mjc8g8ospqfu8sttcz.png)

Let's get started :point_down: 

---

#####Before we go into detail regarding _caching_, let's have a look at how `ORM` tools **handle the given request**.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vc376axt5s9dojukyxg.png)

* when _client_ access our application, our application,  which uses `ORM` tools like `hibernate` to read the data from the database,  will execute a **select query internally** on the database table to _retrieve the data_.

* The **data** is then converted into an **object**, which is then passed to our _application_, which then sends it back to the _client_ as required.

* Every time a _client_ requests data from our _application_ or `ORM` tool, the select statement will be executed, and the process will <u>repeat again</u>.

* Instead of repeatedly performing the same _read operation_, we employ **caching or a cache**.

###Caching :

**Caching** stores data or objects in _temporary locations_. When a request comes in for the **first time**, this `ORM` tool or the `caching framework` will read the data, transform it to an object, and store it in a _temporary location or on disc_. 

The next time a request comes in, these `ORM` frameworks will check to see if the _data_ for that _request is already in the cache_. If it exists,  **no database select queries or database communication will be performed; simple take the object from cache, process it, and return it to the client**.

> Finally, we may increase our application's performance by using  the Cache.

Let's get started with the implementation :point_down:

---

######Follow the steps below to utilise Hazel Cast or any other cache provider.
</br>
  1. Adding Dependencies 
  
  2. Create Cache Configuration 

  3. Enable and Use Caching

  4. Caching in Action

</br>


##1. Adding Dependencies : 

Add the following dependencies in POM.xml file.

* spring-boot-starter-cache
* hazelcast-spring</span>

######POM.xml

```java
<?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>2.0.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.priya.springweb</groupId>
	<artifactId>ProductRestAPI</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>ProductRestAPI</name>
	<description>Demo project for Spring BootRestAPI</description>
	<properties>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
 <dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-cache</artifactId>
		</dependency>
<!-- https://mvnrepository.com/artifact/com.hazelcast/hazelcast-spring -->
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-spring</artifactId>
  
</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</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>

```
##2. Create Cache Configuration :

Now we'll create the **java configuration class** that we'll need to create a `Cache`. This is where we'll give the `Cache` a name, size, and amount of objects that can be placed in it, among other things.

######we can  add `Hazelcast` configuration in one of the following manners:

* Add _hazelcast.yaml_ configuration OR
* Add _hazelcast.xml_ configuration OR
* Define @Bean with _Hazelcast_ configuration in the source code

> I am using the Java Based Configuration using @Bean


```java
package com.priya.springweb.cacheconfiguraions;



import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.hazelcast.config.Config;
import com.hazelcast.config.EvictionConfig;
import com.hazelcast.config.EvictionPolicy;
import com.hazelcast.config.MapConfig;
import com.hazelcast.config.MaxSizeConfig;


@Configuration
public class ProductCacheConfig {

	@Bean
	public Config cacheConfig() {
		return new Config()
				.setInstanceName("hazel-instance")
				.addMapConfig(new MapConfig()
						.setName("product-cache")
						.setTimeToLiveSeconds(5000)
						.setMaxSizeConfig(new MaxSizeConfig(200,com.hazelcast.config.MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
						.setEvictionPolicy(EvictionPolicy.LRU)
						);
				}
}

```
* <b>addMapConfig :</b> We set all the values for the `Cache` in this _MapConfig_, such as name, size, time, and so on.It holds all the _information_ for the `Cache`.

* <b>setName :</b> The name of the cache should be specified. choose whatever name you want.

* <b>setTimeToLiveSeconds :</b> This is the _period of time_ the object will live in the `Cache` before being <u>evicted</u>.

* <b>setMaxSizeConfig :</b> The Cache's `MaxSizeConfig` option has two values: one is the _maximum number of objects the Cache can hold_, and the other is the _maximum amount of space the Cache can maintain_.

* <b>setEvictionPolicy : </b>We'll also need to give `cache eviction information`, such as when the _cache should be cleaned_, because the cache size will grow with time, and if we don't clear it, <u>our application will crash</u>.

> <h6>We can employ a variety of cache policies, like as<h6>

> 1 Least Recently Used(LRU)

> 2 Least Frequently Used(LFU)

>  3 <b>Random :</b>  Randomly pick the element from Cache and removed it.

>  4 <b>NONE :</b> It will do nothing.

---

##3. Enable and Use Caching : 

I'm using the `Rest APIs` that we created in my **previous blog**. 

This should be referred to [https://dev.to/priyanka_nandula/create-rest-crud-api-with-springboot-springdata-jpa-3m6b]

```java
package com.priya.springweb;

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


@SpringBootApplication
@EnableCaching
public class ProductRestApiApplication {

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

}

```
> <b>@EnableCaching</b> that will enable the caching for our application

```java
package com.priya.springweb.entities;

import java.io.Serializable;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class Product implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	private String name;
	private String description;
	private int price;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getDescription() {
		return description;
	}
	public void setDescription(String description) {
		this.description = description;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	
}

```


>  Our Model class Should **implements** the `Serializable` interface because _hazel cast_ or any other `Cache providers` like _EH Cache_ will <u>serialize</u> our projects or `Model Entities` to a file system or `in memory` as required to a database.
```Java
package com.priya.springweb.controllers;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
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.RestController;

import com.priya.springweb.entities.Product;
import com.priya.springweb.repos.ProductRepository;

@RestController
public class ProductController {
	
	@Autowired
	private ProductRepository repository;

	@RequestMapping(value="/products",method=RequestMethod.GET)
	public List<Product> getProducts(){
		
		return repository.findAll();
	}
	
	@RequestMapping(value="/products/{id}",method=RequestMethod.GET)
	@Cacheable(value="product-cache",key="#id")
	public Product getProductById(@PathVariable("id") int id) {
		
		return repository.findById(id).get();
		
	}
	
	@RequestMapping(value="/products/",method=RequestMethod.POST)
	public Product createProduct(@RequestBody Product product) {
		
		return repository.save(product);
	}
	
	
	@RequestMapping(value="/products/",method=RequestMethod.PUT)
	public Product updateProduct(@RequestBody Product product) {
		
		return repository.save(product);
	}
	
	@RequestMapping(value="/products/{id}",method=RequestMethod.DELETE)
	@CacheEvict(value="product-cache",key="#id")
	public void  deleteProduct(@PathVariable("id") int id) {
		
	repository.deleteById(id);
		
	}
}

```

* We will be  using Caching on the `get a single product method`.

* <b>Cacheable : </b> we will also use `@Cachable` on the rest _controllers or service classes_. Here **value is the Cache name we provided in the configuration class** and Key is unique Key to store in `Cache`.

* <b>CacheEvict : </b> _cache evict_ information that is when should cache be cleaned.

> After you made all these Changes Run the Application -->Run on Spring Boot App

---

##4. Caching in Action : 

To check the working of Caching we will execute the `GET MEthod`.

> NOTE: Please remember the following few points.
> 1. Make sure MySQL Server is running.
> 2. Make sure you are running the SpringBoot project in embedded Tomcat Server 


####I'm using `RestTemplate` inside the JUnit Class to consume the Restful Services we created  in the Controller Class.

</br>

* Using the `RestTemplate` given by the spring web module, we can now **consume restful web services or create a restful client**. 

</br>

* `Spring web` enables us to both _design_ and _consume restful web services_.

</br> 

* `Rest Template` is a class that performs various `HTTP` _Methods(GET/PUT/POST/DELETE..)_.

</br>

*  The returned `json` is automatically <u>deserialized</u> into the **Product class** when using a `spring or rest template`.

</br>

######Application.Properties File : 
```java
spring.datasource.url=jdbc:mysql://localhost:3306/myproducts
spring.datasource.username=root
spring.datasource.password=root

server.servlet.context-path=/productapi

productresapi.services.url=http://localhost:8080/productapi/products/

spring.cache.cache-names=product-cache
spring.cache.type=hazelcast
spring.jpa.show-sql=true
```

</br>

```java
package com.priya.springweb;

import static org.junit.Assert.assertEquals;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

import com.priya.springweb.entities.Product;


@RunWith(SpringRunner.class)
@SpringBootTest()
public class ProductRestApiApplicationTests {
	
	@Value("${productresapi.services.url}")
	private String baseURL;

	@Test
	public void textGetProduct() {
		RestTemplate template=new RestTemplate();
		Product product=template.getForObject(baseURL+"2", Product.class);
		System.out.println("name :"+product.getName());
		assertEquals("Mac Book Pro", product.getName());
		
		
		
	}

}

```


######Available methods are:

* <b>getForObject(url, classType)</b> – retrieve a representation by doing a `GET` on the `URL`. The response (if any) is unmarshalled to given class type and returned.

</br>

* <b>getForEntity(url, responseType)</b> – retrieve a representation as `ResponseEntity` by doing a `GET` on the `URL`.

</br>

* <b>exchange(requestEntity, responseType)</b> – execute the specified `RequestEntity` and return the response as `ResponseEntity`.

</br>

* <b>execute(url, httpMethod, requestCallback, responseExtractor)</b> – execute the httpMethod to the given `URI` template, preparing the request with the `RequestCallback`, and reading the response with a `ResponseExtractor`.

</br>

_"In my upcoming `blogs`, I'll go over `RestTemplate` in greater detail.."_

######Now Run this `JUnit Test` Case:

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8v5fpqomtazj1s20q7up.png)

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l13kq7na8ul3g6mdp7ey.png)
  
> Test Runs Successfully. Now go through the `logs Console`.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vrz4e8697vtvvbyvr01o.png)

#####As you can see, when we sent the Request for the first time, it executed the Select Query to get the data from the database.


####Run the JUnit Test Case Again : 

> I've now cleared the `console logs` and _rerunning the Test Case_.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kx6zf8tizs0zjpalhqza.png)

**Now `Hibernate` did not execute the _select query_ because the object was already `cached`**.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u3ekj7dvm7bicyn64hcg.png)

</br>
 

> NOTE : Instead of using `RestTemplate` within the Code, you can also use `Postmansuite`and then check the `logs Console`.

> `RestTemplate` can be used anywhere in the application, such as in the **RestController** or **Service classes**. 

</br>


Here's a link to the code's GIT repository.[https://github.com/priyankanandula/SpringBoot/tree/feature/HazelCast_Cache/ProductRestAPI] 

</br>

That's it, guys, that's how we use `Hazel Cast` to implement caching. I hope you found it _interesting_.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqzw48o9e3u9mwb98g7h.png)


This is _priyanka nandula_ signing off...