Cache with Spring Boot and Hazelcast
Welcome, in this tutorial, we will implement a Hazelcast Cache example in a Spring Boot app.
Hazelcast is used to implement the cache in a distributed environment. This cache mechanism approach offers reliable performance and effective consistency. Hazelcast is a distributed, highly available, and scalable caching mechanism.
1. Introduction
Before going further in this tutorial, we will look at the common terminology such as introduction to Spring Boot and Lombok.
1.1 Spring Boot
- Spring boot is a module that provides rapid application development feature to the spring framework including auto-configuration, standalone-code, and production-ready code
- It creates applications that are packaged as jar and are directly started using embedded servlet containers (such as Tomcat, Jetty or, Undertow). Thus, no need to deploy the war files
- It simplifies the maven configuration by providing the starter template and helps to resolve the dependency conflicts. It automatically identifies the required dependencies and imports them into the application
- It helps in removing the boilerplate code, extra annotations, and xml configurations
- It provides powerful batch processing and manages the rest endpoints
- It provides an efficient jpa-starter library to effectively connect the application with the relational databases
- It offers a Microservice architecture and cloud configuration that manages all the application related configuration properties in a centralized manner
1.2 Lombok
- Lombok is nothing but a small library that reduces the amount of boilerplate Java code from the project
- Automatically generates the getters and setters for the object by using the Lombok annotations
- Hooks in via the Annotation processor API
- Raw source code is passed to Lombok for code generation before the Java Compiler continues. Thus, produces properly compiled Java code in conjunction with the Java Compiler
- Under the
target/classes
folder you can view the compiled class files - Can be used with Maven, Gradle IDE, etc.
1.2.1 Lombok features
Feature | Details |
---|---|
val | Local variables are declared as final |
var | Mutable local variables |
@Slf4J | Creates an SLF4J logger |
@Cleanup | Will call close() on the resource in the finally block |
@Getter | Creates getter methods for all properties |
@Setter | Creates setter for all non-final properties |
@EqualsAndHashCode |
|
@ToString |
|
@NoArgsConstructor |
|
@RequiredArgsContructor |
|
@AllArgsConstructor |
|
@Data |
|
@Builder |
|
@Value |
|
Let us go ahead with the tutorial implementation but before going any further I’m assuming that you’re aware of the Spring boot basics.
2. Cache with Spring Boot and Hazelcast
2.1 Tools Used for Spring boot application and Project Structure
We are using Eclipse Kepler SR2, JDK 8, and Maven. In case you’re confused about where you should create the corresponding files or folder, let us review the project structure of the spring boot application.
Let us start building the application!
3. Hazelcast on Docker
To start with this tutorial, I am hoping you have the hazelcast up and running in your localhost environment. For easy setup, I have the hazelcast up and running on the Docker environment. You can execute the below script using the docker-compose
command to get the hazelcast container running on Docker in minutes. If you’re doing it for the first time the docker image will be downloaded from the docker hub.
hazelcast-docker-compose.yml
services: hazelcast: container_name: hazelcast environment: HZ_NETWORK_PUBLICADDRESS: "127.0.0.1:5701" HZ_NETWORK_RESTAPI_ENABLED: "true" image: "hazelcast/hazelcast:4.0.1" ports: - "5701:5701" management-center: container_name: management-center depends_on: - hazelcast environment: MC_ADMIN_PASSWORD: myPassword11 MC_ADMIN_USER: admin MC_INIT_CMD: "./mc-conf.sh cluster add -H=/data -ma hazelcast:5701 -cn dev" image: "hazelcast/management-center:4.0.3" ports: - "10080:8080" version: "3"
If everything goes well the container would be started successfully as shown in Fig. 1. You can use the docker ps -a
command to confirm that the container is started successfully. For further information on docker basics, you can navigate to this tutorial.
4. Creating a Spring Boot application
Below are the steps involved in developing the application.
4.1 Maven Dependency
Here, we specify the dependency for the Spring boot (Web and JPA, Spring doc Open API (for swagger interface)), H2 database, Java Faker (to generate the dummy data), Lombok, and Hazelcast. The updated file will have the following code.
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" 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.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.springboot.cache.hazelcast</groupId> <artifactId>SpringbootHazelcast</artifactId> <version>0.0.1-SNAPSHOT</version> <name>SpringbootHazelcast</name> <description>Hazelcast cache implementation in spring boot</description> <properties> <java.version>1.8</java.version> <faker.version>1.0.2</faker.version> <spingdoc.openapi.version>1.5.3</spingdoc.openapi.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-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.github.javafaker/javafaker --> <dependency> <groupId>com.github.javafaker</groupId> <artifactId>javafaker</artifactId> <version>${faker.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/com.hazelcast/hazelcast --> <dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast</artifactId> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>${spingdoc.openapi.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
4.2 Application yml file
Create a new YML file at the location: SpringbootHazelcast/src/main/resources/
and add the following code where we will define –
- The h2 database connection and hibernate details
- The h2 console details will be accessible at the following URL –
http://localhost:10091/h2-console
in the browser - The Swagger UI path will be accessible at the following URL –
http://localhost:10091/swagger-ui-custom.html
in the browser
You’re free to change the application or the database details as per your wish.
application.yml
server: error: include-stacktrace: never port: 10091 spring: application: name: springboot-cache-hazelcast datasource: driverClassName: org.h2.Driver password: '' url: 'jdbc:h2:mem:testdb' username: sa h2: console: enabled: true path: /h2-console jpa: database-platform: org.hibernate.dialect.H2Dialect hibernate: ddl-auto: create-drop properties: hibernate: show_sql: true springdoc: swagger-ui: path: /swagger-ui-custom.html
4.3 Java Classes
Let us write the important java class(es) involved in this application. For brevity, we will skip the following classes –
Employee.java
– Entity class that will be persisted in the databaseEmployeeRepository.java
– Repository interface that extends theJpaRepository
interface to perform the SQL operationsEmployeeService.java
– Service class that interact with the DAO layer methodsDefaultEmployeesLoader.java
– Bootstrap class to populate dummy data to the h2 database once the application is started successfullyEntityNotFoundException.java
– Exception class for throwing the not found exception when the entity is not present in the database. The exception class is annotated with the HTTP 404 error response code
4.3.1 Implementation/Main class
Add the following code to the main class to bootstrap the application from the main method. Always remember, the entry point of the spring boot application is the class containing @SpringBootApplication
annotation and the static main method.
SpringbootHazelcastApplication.java
package com.springboot.cache.hazelcast; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; //lombok annotation @Slf4j //spring annotation @SpringBootApplication public class SpringbootHazelcastApplication { public static void main(String[] args) { SpringApplication.run(SpringbootHazelcastApplication.class, args); log.info("Springboot and hazelcast cache application started successfully"); } }
4.3.2 Configuration class
Add the following code to the configuration class where we will define the Faker and Hazelcast cache configuration.
BeanConfig.java
package com.springboot.cache.hazelcast.config; import com.github.javafaker.Faker; import com.hazelcast.config.Config; import com.hazelcast.config.ManagementCenterConfig; import com.hazelcast.core.Hazelcast; import com.hazelcast.core.HazelcastInstance; import com.springboot.cache.hazelcast.model.Employee; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.Locale; import java.util.Map; //spring annotation @Configuration public class BeanConfig { @Bean public Faker faker() { return new Faker(new Locale("en-US")); } //hazelcast cache configuration @Bean public Config config() { final Config cfg = new Config(); cfg.getGroupConfig().setName("dev"); cfg.setManagementCenterConfig(manCenterCfg()); return cfg; } @Bean public Map<integer, employee=""> employeeMap(final HazelcastInstance instance) { return instance.getMap("employeeMap"); } @Bean public HazelcastInstance instance(final Config cfg) { return Hazelcast.newHazelcastInstance(cfg); } private ManagementCenterConfig manCenterCfg() { return new ManagementCenterConfig() .setEnabled(true) // port number should be same on which the hazelcast management center is running .setUrl("http://localhost:10080/hazelcast-mancenter"); } } </integer,>
4.3.3 Controller class
Add the following code to the controller class to specify the different endpoints. The controller methods are annotated with the HTTP GET mapping annotation. The methods will return the response but first checks the data from the hazelcast cache. If present in the cache it will return it from the cache otherwise from the database.
EmployeeController.java
package com.springboot.cache.hazelcast.controller; import com.springboot.cache.hazelcast.model.Employee; import com.springboot.cache.hazelcast.service.EmployeeService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; import java.util.Objects; //lombok annotation @Slf4j //spring annotation @RestController @RequestMapping(path = "/employee") public class EmployeeController { @Autowired private EmployeeService service; @Autowired private Map<Integer, Employee> employeeMap; //get employee by id @GetMapping(path = "/get/{id}") @ResponseStatus(code = HttpStatus.OK) public Employee getEmployee(@PathVariable("id") int id) { //first check if employeeMap has the employee details for the given employee id //if yes then return it. else fetch ir from the database return Objects.nonNull(employeeMap.get(id)) ? employeeMap.get(id) : service.getEmployee(id); } //get all employees @GetMapping(path = "/get") @ResponseStatus(code = HttpStatus.OK) public List<Employee> getEmployees() { //fetch all employees from the database final List<Employee> employees = service.getEmployees(); if (CollectionUtils.isEmpty(employees)) { log.info("Returning empty list"); } else { //add employee to the cache log.info("Adding employees to the cache"); for (final Employee e : employees) { employeeMap.put(e.getId(), e); } } return employees; } }
5. Run the Application
To execute the application, right-click on the SpringbootHazelcastApplication.java
class, Run As -> Java Application
.
// Fig. 3: Run the Application
6. Project Demo
When the application is started, open the Postman tool to hit the application endpoints. You are free to choose any tool of your choice and for this tutorial, we will use the spring swagger interface (accessible at the following URL – http://localhost:10091/swagger-ui-custom.html
).
Application endpoints
-- HTTP GET endpoints – //Endpoint name – Get all employees //URL :: http://localhost:10091/employee/get //Endpoint name – Get employee by id //URL :: http://localhost:10091/employee/get/101
That is all for this tutorial and I hope the article served you whatever you were looking for. Happy Learning and do not forget to share!
7. Summary
In this section, you learned:
- Spring boot and Hazelcast cache introduction
- Steps to implement hazelcast in a spring boot application
You can download the sample application as an Eclipse project in the Downloads section.
8. Download the Project
This was an example of how to implement Hazelcast cache in a spring boot application.
You can download the full source code of this example here: Cache with Spring Boot and Hazelcast