Hazelcast is an in-memory distributed caching mechanism. In this tutorial, I am going to show you how to enable the Spring Boot Hazelcast cache.
Spring Boot Hazelcast Cache:
Hazelcast is an in-memory caching mechanism provided by the spring boot. Spring boot auto-configures the Hazelcast instance if the hazelcast is available in our application’s classpath and the required configuration is available.
Spring Boot Hazelcast Example:
Here I am going to create a simple spring boot rest service to read Items from the database using the hazelcast cache.
Technologies:
- Spring Boot 2.0.5 RELEASE
- Hazelcast
- Spring Boot JDBC
- MySQL
- Java 8
Project Structure:
Hazelcast Dependency:
Add the below hazelcast dependencies in pom.xml
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
</dependency>
Complete 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.onlinetutorialspoint</groupId>
<artifactId>SpringBoot_Hazelcast_Cache_Example</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SpringBoot_Hazelcast_Cache_Example</name>
<description>Spring Boot Hazelcast Cache Example</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast</artifactId>
</dependency>
<dependency>
<groupId>com.hazelcast</groupId>
<artifactId>hazelcast-spring</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Database configuration details.
server.port=8080
spring.datasource.driver-class-name: com.mysql.jdbc.Driver
spring.datasource.url: jdbc:mysql://localhost:3306/otp
spring.datasource.username: root
spring.datasource.password: Chandu@123
Hazelcast configuration.
Creating a Hazelcast Config bean with the required configuration.
package com.onlinetutorialspoint.config;
import com.hazelcast.config.Config;
import com.hazelcast.config.EvictionPolicy;
import com.hazelcast.config.MapConfig;
import com.hazelcast.config.MaxSizeConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HazelcastCacheConfig {
@Bean
public Config hazelcastConfig(){
return new Config().setInstanceName("hazelcast-instance")
.addMapConfig(new MapConfig().setName("itemCache")
.setMaxSizeConfig(new MaxSizeConfig(300,MaxSizeConfig.MaxSizePolicy.FREE_HEAP_SIZE))
.setEvictionPolicy(EvictionPolicy.LRU)
.setTimeToLiveSeconds(2000));
}
}
Mapping the itemCache to hazelCache config and giving eviction policy as LRU (Last Recently Used).
Preparing ItemCache component.
package com.onlinetutorialspoint.cache;
import com.onlinetutorialspoint.model.Item;
import com.onlinetutorialspoint.repo.ItemRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
@Component
public class ItemCache {
@Autowired
ItemRepository itemRepo;
@Cacheable(value="itemCache", key="#id")
public Item getItem(int id){
System.out.println("In ItemCache Component..");
Item item = null;
try{
item = itemRepo.getItem(id);
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}
return item;
}
@CacheEvict(value="itemCache",key = "#id")
public int deleteItem(int id){
System.out.println("In ItemCache Component..");
return itemRepo.deleteItem(id);
}
@CachePut(value="itemCache")
public void updateItem(Item item){
System.out.println("In ItemCache Component..");
itemRepo.updateItem(item);
}
}
@Cachable: Is used to adding the cache behavior to a method. We can also give the name to it, where the cache results would be saved.
@CacheEvict: Is used to remove the one or more cached values. allEntries=true parameter allows us to remove all entries from the cache.
@CachePut: Is used to update the cached value.
Creating an Item model
package com.onlinetutorialspoint.model;
import java.io.Serializable;
public class Item implements Serializable {
private int id;
private String name;
private String category;
public Item() {
}
public Item(int id, String name, String category) {
this.id = id;
this.name = name;
this.category = category;
}
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 getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
}
Creating Item Repository responsible to read items from MySQL database using JdbcTemplate.
package com.onlinetutorialspoint.repo;
import com.onlinetutorialspoint.model.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class ItemRepository {
@Autowired
JdbcTemplate template;
/*Getting a specific item by item id from table*/
public Item getItem(int itemId){
System.out.println("Reading Item From Repository..");
String query = "SELECT * FROM ITEM WHERE ID=?";
return template.queryForObject(query,new Object[]{itemId},new BeanPropertyRowMapper<>(Item.class));
}
/*delete an item from database*/
public int deleteItem(int id){
String query = "DELETE FROM ITEM WHERE ID =?";
int size = template.update(query,id);
return size;
}
/*update an item from database*/
public void updateItem(Item item){
String query = "UPDATE ITEM SET name=?, category=? WHERE id =?";
template.update(query,
new Object[] {
item.getName(),item.getCategory(), Integer.valueOf(item.getId())
});
}
}
Creating rest endpoints to access item data
package com.onlinetutorialspoint.controller;
import com.onlinetutorialspoint.cache.ItemCache;
import com.onlinetutorialspoint.model.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
public class ItemController {
@Autowired
ItemCache itemCache;
@GetMapping("/item/{itemId}")
@ResponseBody
public ResponseEntity<Item> getItem(@PathVariable int itemId){
System.out.println("RestController..");
long start = System.currentTimeMillis();
Item item = itemCache.getItem(itemId);
long end = System.currentTimeMillis();
System.out.println("Took : " + ((end - start) / 1000+" sec."));
return new ResponseEntity<Item>(item, HttpStatus.OK);
}
@PutMapping("/updateItem")
@ResponseBody
public ResponseEntity<Item> updateItem(@RequestBody Item item){
if(item != null){
itemCache.updateItem(item);
}
return new ResponseEntity<Item>(item, HttpStatus.OK);
}
@DeleteMapping("/delete/{id}")
@ResponseBody
public ResponseEntity<Void> deleteItem(@PathVariable int id){
itemCache.deleteItem(id);
return new ResponseEntity<Void>(HttpStatus.ACCEPTED);
}
}
Main class with the @EnablingCache annotation to enable the cache.
package com.onlinetutorialspoint;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class SpringBootHazelcastCacheExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootHazelcastCacheExampleApplication.class, args);
}
}
Run the application:
mvn spring-boot:run
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.5.RELEASE)
2018-10-14 17:43:02.414 INFO 5196 --- [ main] ringBootHazelcastCacheExampleApplication : Starting SpringBootHazelcastCacheExampleApplication on DESKTOP-RN4SMHT with PID 5196 (E:\work\SpringBoot_Hazelcast_Cache_Example\target\classes started by Lenovo in E:\work\SpringBoot_Hazelcast_Cache_Example)
2018-10-14 17:43:02.414 INFO 5196 --- [ main] ringBootHazelcastCacheExampleApplication : No active profile set, falling back to default profiles: default
.....
.....
Access the application
Console logs:
RestController..
In ItemCache Component..
Reading Item From Repository..
Took : 3 sec.
For the first time when we access the item 1, it took 3 Sec to get the data from the database.
Accessing the same item again and observe the log statements.
RestController..
Took : 0 sec.
For the second time, it took zero seconds to get the same data because for this time the data came from the hazelCast cache.
Happy Learning 🙂