Signed in as @priyankanandula
After few days gap, I am back 😎
Good day 😃, 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
Let's get started 👇
Before we go into detail regarding caching, let's have a look at how ORM
tools handle the given request.
when client access our application, our application, which uses
ORM
tools likehibernate
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 repeat again.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 👇
Follow the steps below to utilise Hazel Cast or any other cache provider.
Adding Dependencies
Create Cache Configuration
Enable and Use Caching
Caching in Action
1. Adding Dependencies :
Add the following dependencies in POM.xml file.
- spring-boot-starter-cache
- hazelcast-spring
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>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
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)
);
}
}
addMapConfig : 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 theCache
.setName : The name of the cache should be specified. choose whatever name you want.
setTimeToLiveSeconds : This is the period of time the object will live in the
Cache
before being evicted.setMaxSizeConfig : 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.setEvictionPolicy : 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, our application will crash.
We can employ a variety of cache policies, like as
1 Least Recently Used(LRU)
2 Least Frequently Used(LFU)
3 Random : Randomly pick the element from Cache and removed it.
4 NONE : 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]
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);
}
}
@EnableCaching that will enable the caching for our application
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 otherCache providers
like EH Cache will serialize our projects orModel Entities
to a file system orin memory
as required to a database.
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
.Cacheable : 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 inCache
.CacheEvict : 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.
- Make sure MySQL Server is running.
- 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.
- Using the
RestTemplate
given by the spring web module, we can now consume restful web services or create a restful client.
-
Spring web
enables us to both design and consume restful web services.
-
Rest Template
is a class that performs variousHTTP
Methods(GET/PUT/POST/DELETE..).
- The returned
json
is automatically deserialized into the Product class when using aspring or rest template
.
Application.Properties File :
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
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:
-
getForObject(url, classType) – retrieve a representation by doing a
GET
on theURL
. The response (if any) is unmarshalled to given class type and returned.
-
getForEntity(url, responseType) – retrieve a representation as
ResponseEntity
by doing aGET
on theURL
.
-
exchange(requestEntity, responseType) – execute the specified
RequestEntity
and return the response asResponseEntity
.
-
execute(url, httpMethod, requestCallback, responseExtractor) – execute the httpMethod to the given
URI
template, preparing the request with theRequestCallback
, and reading the response with aResponseExtractor
.
"In my upcoming blogs
, I'll go over RestTemplate
in greater detail.."
Now Run this JUnit Test
Case:
Test Runs Successfully. Now go through the
logs Console
.
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.
Now Hibernate
did not execute the select query because the object was already cached
.
NOTE : Instead of using
RestTemplate
within the Code, you can also usePostmansuite
and then check thelogs Console
.
RestTemplate
can be used anywhere in the application, such as in the RestController or Service classes.
Here's a link to the code's GIT repository.[https://github.com/priyankanandula/SpringBoot/tree/feature/HazelCast_Cache/ProductRestAPI]
That's it, guys, that's how we use Hazel Cast
to implement caching. I hope you found it interesting.
This is priyanka nandula signing off...
Top comments (2)
Really awesome and detailed article. I have used different caching techniques in the client side applications like Angular but never thought something similar to that is possible even in the server side. Would definitely want to try out once!
Thank you 😊