We will further need to add the following dependencies to our pom.xml.
The
co.elastic.clients:elasticsearch-java dependency is used to include the official Java client for Elasticsearch in our project.
The Spring Data Elasticsearch project uses this client under the hood to communicate with the Elasticsearch cluster, enabling us to perform various operations such as indexing documents, executing searches, and managing indices.
By including the
jackson-databind dependency in your project, you ensure that the necessary JSON processing capabilities are available to the Elasticsearch Java client.
<?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.2.5</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.javainuse</groupId>
<artifactId>boot-elasticsearch-crud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot-elasticsearch-crud</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-data-elasticsearch</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-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>8.13.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
By default elasticsearch runs on https. For this we will be create a truststore file. This truststore file is then used by your spring boot application to establish a secure SSL/TLS connection with
the Elasticsearch cluster.
In this truststore we will be importing the CA certificate of elasticsearch.
When your application attempts to connect to Elasticsearch, it will use the CA certificate in the truststore to verify
the identity of the Elasticsearch server and establish a trusted connection. For this go to the elasticsearch jdk/bin installation and run the following command
.\keytool.exe -import -file "E:\trial\elasticsearch-8.13.4-windows-x86_64\elasticsearch-8.13.4\config\certs\http_ca.crt" -keystore "E:\trial\elasticsearch-8.13.4-windows-x86_64\elasticsearch-8.13.4\config\certs\truststore.p12" -storepass javainuse -noprompt -storetype pkcs12
The
HttpClientConfigImpl class is a Spring configuration class that implements the HttpClientConfigCallback interface. This interface allows developers to customize the HttpAsyncClientBuilder, which is responsible for creating the client used to communicate with Elasticsearch.
The HttpClientConfigImpl class performs the following tasks:
-
Authentication Setup: The class creates a CredentialsProvider object and sets the username and password credentials required for authentication with the Elasticsearch cluster.
In the provided example, the username is "elastic" and the password is "javainuse".
-
SSL/TLS Configuration: To establish a secure connection with the Elasticsearch cluster, the class loads an SSL/TLS truststore from a specified file path (D:\elasticsearch-8.13.4-windows-x86\_64\elasticsearch-8.13.4\config\certs\truststore.p12). The truststore is a file that contains trusted Certificate Authorities (CAs) used for verifying the server's identity during the SSL/TLS handshake.
The class creates an SSLContext object using the SSLContextBuilder and loads the truststore file into it, using the provided password ("password" in this case).
-
Client Configuration: Finally, the class sets the CredentialsProvider and SSLContext on the HttpAsyncClientBuilder. This builder is then used to create the Elasticsearch client, ensuring that the client
can authenticate with the Elasticsearch cluster using the provided credentials and establish a secure connection using the specified truststore.
package com.javainuse.bootelastisearchcrud.config;
import java.io.File;
import javax.net.ssl.SSLContext;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts;
import org.elasticsearch.client.RestClientBuilder.HttpClientConfigCallback;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HttpClientConfigImpl implements HttpClientConfigCallback {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
try {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
UsernamePasswordCredentials usernamePasswordCredentials = new UsernamePasswordCredentials("elastic",
"javainuse");
credentialsProvider.setCredentials(AuthScope.ANY, usernamePasswordCredentials);
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
String trustLocationStore = "E:\\trial\\elasticsearch-8.13.4-windows-x86_64\\elasticsearch-8.13.4\\config\\certs\\truststore.p12";
File trustLocationFile = new File(trustLocationStore);
SSLContextBuilder sslContextBuilder = SSLContexts.custom().loadTrustMaterial(trustLocationFile,
"javainuse".toCharArray());
SSLContext sslContext = sslContextBuilder.build();
httpClientBuilder.setSSLContext(sslContext);
} catch (Exception e) {
}
return httpClientBuilder;
}
}
Previously we discussed how to configure the Elasticsearch client to establish a secure and authenticated connection with the Elasticsearch cluster.
However, to fully integrate Elasticsearch into our Spring Boot application, we need to create a bean that encapsulates the client configuration and provides an instance of the ElasticsearchClient class.
The ESClient class, annotated with @Component, serves this purpose. Let's break down the getElasticsearchClient method within this class:
-
Initializing the RestClientBuilder:
The RestClientBuilder is initialized with an HttpHost object, which specifies the hostname, port, and protocol for connecting to the Elasticsearch cluster. In this example, the client is configured to connect to a local Elasticsearch instance running on http://localhost:9200.
-
Setting the HttpClientConfigCallback:
The HttpClientConfigCallback implementation, HttpClientConfigImpl, is instantiated and set on the RestClientBuilder. This callback is responsible for configuring the HttpAsyncClientBuilder with the necessary credentials and SSL/TLS settings, as discussed in the previous section.
-
Building the RestClient:
The RestClient instance is created using the configured RestClientBuilder.
-
Creating the RestClientTransport:
The RestClientTransport is a wrapper around the RestClient that provides a low-level transport layer for communicating with the Elasticsearch cluster. It is initialized with the RestClient instance and a JacksonJsonpMapper for handling JSON serialization and deserialization.
-
Instantiating the ElasticsearchClient:
Finally, an ElasticsearchClient instance is created using the RestClientTransport. This client provides a high-level API for interacting with the Elasticsearch cluster, allowing developers to perform various operations such as indexing documents, executing searches, and managing indices.
package com.javainuse.bootelastisearchcrud.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport;
@Component
public class ESClient {
@Bean
public ElasticsearchClient getElasticsearchClient() {
RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9200, "https"));
RestClientBuilder.HttpClientConfigCallback httpClientConfigCallback = new HttpClientConfigImpl();
builder.setHttpClientConfigCallback(httpClientConfigCallback);
RestClient restClient = builder.build();
RestClientTransport restClientTransport = new RestClientTransport(restClient, new JacksonJsonpMapper());
return new ElasticsearchClient(restClientTransport);
}
}
Next we create an document class named Employee. This is the class which will map the employee to the elasticsearch index named employee.
In Elasticsearch, data is stored in the form of documents, which are organized into indices. When working with Elasticsearch from a Java application, it is often necessary to map Java objects to Elasticsearch documents and vice versa. Spring Data Elasticsearch
provides annotations that simplify this mapping process.
package com.javainuse.bootelastisearchcrud.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
@Document(indexName = "employee")
public class Employee {
@Id
private String id;
private String name;
private String department;
public Employee() {
}
public Employee(String id, String name, String department) {
super();
this.id = id;
this.name = name;
this.department = department;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + ", department=" + department + "]";
}
}
Spring Data Elasticsearch provides a convenient way to interact with Elasticsearch by leveraging the repository pattern. By defining an interface that extends ElasticsearchRepository, Spring Data Elasticsearch automatically generates an implementation for common CRUD operations and custom queries on Elasticsearch documents.
By extending ElasticsearchRepository
, the EmployeeRepository interface inherits a set of pre-defined methods for common CRUD operations on Employee documents in Elasticsearch. These methods include:
-
save(Entity): Saves a new entity or updates an existing one.
- findById(ID): Retrieves an entity by its ID.
- findAll(): Retrieves all entities.
- deleteById(ID): Deletes an entity by its ID.
- deleteAll(): Deletes all entities.
Create the EmployeeRepository class as follows -
package com.javainuse.springbootelasticsearchcrud.repository;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import com.javainuse.springbootelasticsearchcrud.model.Employee;
public interface EmployeeRepository extends ElasticsearchRepository<Employee, String> {
}
If we now start the spring boot application and check the elasticsearch indices, an index named employee has been created.
We can check this by using url - https://localhost:9200/_cat/indices?v
Create Employee API
Employee DTO (Data Transfer Object)
Using DTO classes allows for a clear separation between the presentation layer and the domain layer.
DTOs provide a way to transfer data between different layers, such as the
controller and service layers, without leaking unnecessary information or domain-specific logic.
Create the EmployeeDto class as follows-
package com.javainuse.bootelastisearchcrud.dto;
public class EmployeeDto {
private String id;
private String name;
private String department;
public EmployeeDto(String id, String name, String department) {
super();
this.setId(id);
this.name = name;
this.department = department;
}
public EmployeeDto() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDepartment() {
return department;
}
public void setDepartment(String department) {
this.department = department;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Employee Mapper Implementation
In the service layer we need to convert EmployeeDto instance to Employee instance and viceversa.
We perform this conversion operation using Employee Mapper.
package com.javainuse.bootelastisearchcrud.mapper;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.model.Employee;
public class EmployeeMapper {
public static EmployeeDto mapToEmployeeDto(Employee employee) {
return new EmployeeDto(employee.getId(), employee.getName(), employee.getDepartment());
}
public static Employee mapToEmployee(EmployeeDto employeeDto) {
return new Employee(employeeDto.getId(), employeeDto.getName(), employeeDto.getDepartment());
}
}
Service Implementation
We will now create the service layer. First let us create an interface named EmployeeService with single method named createEmployee which
will create a new employee record. We are creating the service interface first as we want to follow the programming to an interface
design principle.
package com.javainuse.bootelastisearchcrud.service;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
public interface EmployeeService {
EmployeeDto createEmployee(EmployeeDto employeeDto);
}
Next we implement the service implementation named EmployeeSericeImpl.
This class we autowire the repository.
There is a method called createEmployee which takes an EmployeeDto object
as a parameter. Inside this method, it maps the EmployeeDto object to an Employee
object using a class called EmployeeMapper. Then, we save the employee object to
the database using the employeeRepository.save() method.
Finally, we map the created employee object back to an EmployeeDto object using EmployeeMapper and return it.
package com.javainuse.bootelastisearchcrud.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.mapper.EmployeeMapper;
import com.javainuse.bootelastisearchcrud.model.Employee;
import com.javainuse.bootelastisearchcrud.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public EmployeeDto createEmployee(EmployeeDto employeeDto) {
Employee employee = EmployeeMapper.mapToEmployee(employeeDto);
Employee createdEmployee = employeeRepository.save(employee);
return EmployeeMapper.mapToEmployeeDto(createdEmployee);
}
}
Controller Implementation
EmployeeController class is a REST controller class that handles HTTP requests related to employee operations.
It uses Spring's @RestController annotation to indicate that the
class is a REST controller that facilitates the mapping of requests to methods.
The class has a dependency on the EmployeeService class, which is injected
using the @Autowired annotation. This allows the class to utilize the methods provided by the
EmployeeService to perform employee-related operations.
The class contains a single method, createEmployee, which is mapped to a POST request with the endpoint /employee.
This method takes in an EmployeeDto object as the request body,
representing the employee data to be created. It invokes the createEmployee method from the injected EmployeeService,
passing the employeeDto as an argument. The response from the service method call is then wrapped
in a ResponseEntity object, specifying the created employee DTO and the HTTP status code HttpStatus.CREATED as the response.
package com.javainuse.bootelastisearchcrud.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.service.EmployeeService;
@RestController
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping(value = "/employee")
public ResponseEntity<EmployeeDto> createEmployee(@RequestBody EmployeeDto employeeDto) {
EmployeeDto createdEmployee = employeeService.createEmployee(employeeDto);
return new ResponseEntity<>(createdEmployee, HttpStatus.CREATED);
}
}
Test
Let us now test the create employee API.
If we check elasticsearch
Get Employee By Id API
Service Implementation
Create an exception named EmployeeNotFoundException which will be thrown if the employee with the specified id is not found.
package com.javainuse.bootmysqlcrud.exception;
public class EmployeeNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
public EmployeeNotFoundException() {
}
public EmployeeNotFoundException(String message) {
super(message);
}
}
In the EmployeeService interface, the following method has been added -
EmployeeDto getEmployeeById(Long employeeId) throws EmployeeNotFoundException.
This method is used to retrieve information about an employee based on their unique identifier,
which is provided as the employeeId parameter.
The method returns an EmployeeDto object, which is a DTO (Data Transfer Object) containing the details of the employee.
Additionally, this method throws an EmployeeNotFoundException if no employee with the provided ID is found.
This exception is used to handle cases where the requested employee does not exist in the system or cannot be found.
By throwing this exception, the calling code can handle and respond to such errors appropriately.
package com.javainuse.bootelastisearchcrud.service;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
public interface EmployeeService {
EmployeeDto createEmployee(EmployeeDto employeeDto);
EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException;
}
Next in the EmployeeServiceImpl we implement the
getEmployeeById method of the EmployeeService interface.
This method takes an employeeId as a parameter and returns an EmployeeDto object, which represents a
Data Transfer Object (DTO) carrying employee information.
The method starts by using the employeeRepository, which is an instance of a Spring Data JPA
repository, to retrieve an employee record from the database using the employeeId.
The findById method returns an Optional<Employee> instance which may contain the employee object if it exists.
Next, the code checks if the employee object is empty or not using the isEmpty() method.
If it is empty, it means no employee record was found with the given employeeId.
In such a case, the code throws an EmployeeNotFoundException which is a custom exception class.
The exception message includes the employeeId that was not found.
If the employee object is not empty, it means a valid employee record exists with the given employeeId.
To convert the Employee object to an EmployeeDto object, the code uses a EmployeeMapper class
that provides a static method called mapToEmployeeDto.
The mapToEmployeeDto method takes an Employee object as input and returns an EmployeeDto object.
Finally, the method returns the Employee Dto object representing the employee found in the database.
package com.javainuse.bootelastisearchcrud.service;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.mapper.EmployeeMapper;
import com.javainuse.bootelastisearchcrud.model.Employee;
import com.javainuse.bootelastisearchcrud.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public EmployeeDto createEmployee(EmployeeDto employeeDto) {
Employee employee = EmployeeMapper.mapToEmployee(employeeDto);
Employee createdEmployee = employeeRepository.save(employee);
return EmployeeMapper.mapToEmployeeDto(createdEmployee);
}
@Override
public EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException {
Optional<Employee> employee = employeeRepository.findById(employeeId);
if (employee.isEmpty()) {
throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
}
return EmployeeMapper.mapToEmployeeDto(employee.get());
}
}
Controller Implementation
Next in the controller class we define a GET request method that retrieves details of an employee by their employee ID.
The @GetMapping annotation specifies that this method should handle HTTP GET requests with the specified URL
"/employee/{employeeId}".
The employeeId is a path variable and is extracted from the URL using the @PathVariable annotation.
The method throws an EmployeeNotFoundException if the employee with the given ID is not found.
Inside the method, it delegates the responsibility of retrieving the employee details to the employeeService
which is an instance of the EmployeeService class.
It calls the getEmployeeById method of the employeeService by passing the employeeId as a parameter.
The result, which is an instance of EmployeeDto, is then returned as the response with a status code of OK (200) using
the ResponseEntity class.
If the requested employee is not found, it catches the EmployeeNotFoundException and re-throws it,
allowing the exception to be handled by any exception handler defined in the application.
package com.javainuse.bootelastisearchcrud.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.service.EmployeeService;
@RestController
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping(value = "/employee")
public ResponseEntity<EmployeeDto> createEmployee(@RequestBody EmployeeDto employeeDto) {
EmployeeDto createdEmployee = employeeService.createEmployee(employeeDto);
return new ResponseEntity<>(createdEmployee, HttpStatus.CREATED);
}
@GetMapping(value = "/employee/{employeeId}")
public ResponseEntity<EmployeeDto> getEmployeeById(@PathVariable("employeeId") String employeeId)
throws EmployeeNotFoundException {
try {
EmployeeDto employee = employeeService.getEmployeeById(employeeId);
return new ResponseEntity<>(employee, HttpStatus.OK);
} catch (EmployeeNotFoundException employeeNotFoundException) {
throw employeeNotFoundException;
}
}
}
Test
Let us now test the get employee by id API.
If suppose we try to retrieve an employee that does not exist
Let us now test the get employee by id API.
Get All Employees
Service Implementation
Next we define EmployeeService interface method named getEmployees().
This method returns a list of EmployeeDto objects.
package com.javainuse.bootelastisearchcrud.service;
import java.util.List;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
public interface EmployeeService {
EmployeeDto createEmployee(EmployeeDto employeeDto);
EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException;
List<EmployeeDto> getEmployees();
}
In the EmployeeServiceImpl, we define the getEmployees method that overrides a method with
the same signature in the EmployeeService interface. It returns a list of EmployeeDto objects.
Inside the method, it uses the employeeRepository to retrieve a list
of Employee objects from elasticsearch.
Then, it uses the stream method on the list of Employee objects to create a stream,
and applies a map operation to each element of the stream.
The map operation takes a lambda expression that maps each Employee object to an
EmployeeDto object using the EmployeeMapper.mapToEmployeeDto method.
Finally, it uses the collect operation to collect the mapped EmployeeDto objects into a list and returns that list.
package com.javainuse.bootelastisearchcrud.service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.mapper.EmployeeMapper;
import com.javainuse.bootelastisearchcrud.model.Employee;
import com.javainuse.bootelastisearchcrud.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public EmployeeDto createEmployee(EmployeeDto employeeDto) {
Employee employee = EmployeeMapper.mapToEmployee(employeeDto);
Employee createdEmployee = employeeRepository.save(employee);
return EmployeeMapper.mapToEmployeeDto(createdEmployee);
}
@Override
public EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException {
Optional<Employee> employee = employeeRepository.findById(employeeId);
if (employee.isEmpty()) {
throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
}
return EmployeeMapper.mapToEmployeeDto(employee.get());
}
@Override
public List<EmployeeDto> getEmployees() {
Iterable<Employee> employeesIterable = employeeRepository.findAll();
List<Employee> employees = StreamSupport.stream(employeesIterable.spliterator(), false).toList();
return employees.stream().map((emp) -> EmployeeMapper.mapToEmployeeDto(emp)).collect(Collectors.toList());
}
}
Controller Implementation
Finally in the EmployeeController class we define the getEmployees method.
@GetMapping(value = "/employees") is a mapping annotation that maps the method to the specific
URL endpoint "/employees" and specifies that it will handle GET requests.
The method implementation retrieves a list of employees using the employeeService.getEmployees() method,
which is responsible for fetching the employee data from a data source such as a database.
The retrieved list of employees is then wrapped in a ResponseEntity, which is an
HTTP response object that allows us to control the HTTP response status and headers. In this case, the
ResponseEntity is being created with the list of employees as the response body and HttpStatus.CREATED as the status code.
Finally, the ResponseEntity object is returned from the method, indicating that the endpoint response will contain a list of employees.
package com.javainuse.bootelastisearchcrud.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.service.EmployeeService;
@RestController
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping(value = "/employee")
public ResponseEntity<EmployeeDto> createEmployee(@RequestBody EmployeeDto employeeDto) {
EmployeeDto createdEmployee = employeeService.createEmployee(employeeDto);
return new ResponseEntity<>(createdEmployee, HttpStatus.CREATED);
}
@GetMapping(value = "/employee/{employeeId}")
public ResponseEntity<EmployeeDto> getEmployeeById(@PathVariable("employeeId") String employeeId)
throws EmployeeNotFoundException {
try {
EmployeeDto employee = employeeService.getEmployeeById(employeeId);
return new ResponseEntity<>(employee, HttpStatus.OK);
} catch (EmployeeNotFoundException employeeNotFoundException) {
throw employeeNotFoundException;
}
}
@GetMapping(value = "/employees")
public ResponseEntity<List<EmployeeDto>> getEmployees() {
List<EmployeeDto> employees = employeeService.getEmployees();
return new ResponseEntity<>(employees, HttpStatus.OK);
}
}
Test
Let us now test the get all employees.
Delete Employee By Id API
Service Implementation
The deleteEmployee method in the EmployeeService interface is used to delete an employee record from a database
or any other persistence mechanism. It takes in the employeeId as a parameter which specifies the unique
identifier for the employee. If the employee with the specified employeeId does not exist, it throws
an EmployeeNotFoundException.
package com.javainuse.bootelastisearchcrud.service;
import java.util.List;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
public interface EmployeeService {
EmployeeDto createEmployee(EmployeeDto employeeDto);
EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException;
List<EmployeeDto> getEmployees();
void deleteEmployee(String employeeId) throws EmployeeNotFoundException;
}
The deleteEmployee method deletes an employee from the employee repository based on their ID.
The method takes in a "Long" parameter called "employeeId," representing the ID of the employee to be deleted.
It uses the "employeeRepository" to find the employee with the given ID by
calling the "findById" method. This method returns an "Optional<Employee>," which may or may not contain a value.
Next, it checks if the "employee" optional is empty using the "isEmpty" method.
If it's empty, it means that the employee with the given ID does not exist in the repository.
In this case, the method throws an "EmployeeNotFoundException" with a specific message mentioning the employee ID.
If the employee exists, the method proceeds to delete the employee from the repository by calling the
"deleteById" method on the "employeeRepository" with the employeeId parameter.
package com.javainuse.bootelastisearchcrud.service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.mapper.EmployeeMapper;
import com.javainuse.bootelastisearchcrud.model.Employee;
import com.javainuse.bootelastisearchcrud.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public EmployeeDto createEmployee(EmployeeDto employeeDto) {
Employee employee = EmployeeMapper.mapToEmployee(employeeDto);
Employee createdEmployee = employeeRepository.save(employee);
return EmployeeMapper.mapToEmployeeDto(createdEmployee);
}
@Override
public EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException {
Optional<Employee> employee = employeeRepository.findById(employeeId);
if (employee.isEmpty()) {
throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
}
return EmployeeMapper.mapToEmployeeDto(employee.get());
}
@Override
public List<EmployeeDto> getEmployees() {
Iterable<Employee> employeesIterable = employeeRepository.findAll();
List<Employee> employees = StreamSupport.stream(employeesIterable.spliterator(), false).toList();
return employees.stream().map((emp) -> EmployeeMapper.mapToEmployeeDto(emp)).collect(Collectors.toList());
}
@Override
public void deleteEmployee(String employeeId) throws EmployeeNotFoundException {
Optional<Employee> employee = employeeRepository.findById(employeeId);
if (employee.isEmpty()) {
throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
}
employeeRepository.deleteById(employeeId);
}
}
Controller Implementation
The deleteEmployees method is a DELETE request mapping method that deletes an employee with the specified employeeId.
It throws an EmployeeNotFoundException if the employee with the specified id is not found.
After deleting the employee, it returns a ResponseEntity with a status of HttpStatus.NO_CONTENT.
package com.javainuse.bootelastisearchcrud.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.service.EmployeeService;
@RestController
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping(value = "/employee")
public ResponseEntity<EmployeeDto> createEmployee(@RequestBody EmployeeDto employeeDto) {
EmployeeDto createdEmployee = employeeService.createEmployee(employeeDto);
return new ResponseEntity<>(createdEmployee, HttpStatus.CREATED);
}
@GetMapping(value = "/employee/{employeeId}")
public ResponseEntity<EmployeeDto> getEmployeeById(@PathVariable("employeeId") String employeeId)
throws EmployeeNotFoundException {
try {
EmployeeDto employee = employeeService.getEmployeeById(employeeId);
return new ResponseEntity<>(employee, HttpStatus.OK);
} catch (EmployeeNotFoundException employeeNotFoundException) {
throw employeeNotFoundException;
}
}
@GetMapping(value = "/employees")
public ResponseEntity<List<EmployeeDto>> getEmployees() {
List<EmployeeDto> employees = employeeService.getEmployees();
return new ResponseEntity<>(employees, HttpStatus.OK);
}
@DeleteMapping(value = "/employee/{employeeId}")
public ResponseEntity<HttpStatus> deleteEmployees(@PathVariable("employeeId") String employeeId)
throws EmployeeNotFoundException {
employeeService.deleteEmployee(employeeId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
Test
Let us now test the delete employee by id api.
Update Employee API
Service Implementation
The updateEmployee method in the EmployeeService interface is used to update the details of an employee.
It takes an EmployeeDto object as a parameter, which contains the updated information for the employee.
The method returns the updated EmployeeDto object after the update is successful.
If the employee with the specified employeeDto does not exist in the system, the method throws an EmployeeNotFoundException,
indicating that the employee could not be updated because they were not found.
package com.javainuse.bootelastisearchcrud.service;
import java.util.List;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
public interface EmployeeService {
EmployeeDto createEmployee(EmployeeDto employeeDto);
EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException;
List<EmployeeDto> getEmployees();
void deleteEmployee(String employeeId) throws EmployeeNotFoundException;
EmployeeDto updateEmployee(EmployeeDto employeeDto) throws EmployeeNotFoundException;
}
The method updateEmployee in the EmployeeServiceImpl class updates an employee record
in the database based on the provided EmployeeDto object.
It first retrieves the employee record from the database using the findById method of the employeeRepository object.
If the retrieved employee is empty (not found), it throws an EmployeeNotFoundException with a message indicating
that the employee with the given ID was not found.
If the employee is found, it updates the employee object with the values from the employeeDto object by
calling the setName and setDepartment methods on the employee object.
It then saves the updated employee object to the database using the save method of the employeeRepository object.
Finally, it returns a mapped EmployeeDto object based on the saved employee object using the mapToEmployeeDto
method of the EmployeeMapper class.
package com.javainuse.bootelastisearchcrud.service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.mapper.EmployeeMapper;
import com.javainuse.bootelastisearchcrud.model.Employee;
import com.javainuse.bootelastisearchcrud.repository.EmployeeRepository;
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public EmployeeDto createEmployee(EmployeeDto employeeDto) {
Employee employee = EmployeeMapper.mapToEmployee(employeeDto);
Employee createdEmployee = employeeRepository.save(employee);
return EmployeeMapper.mapToEmployeeDto(createdEmployee);
}
@Override
public EmployeeDto getEmployeeById(String employeeId) throws EmployeeNotFoundException {
Optional<Employee> employee = employeeRepository.findById(employeeId);
if (employee.isEmpty()) {
throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
}
return EmployeeMapper.mapToEmployeeDto(employee.get());
}
@Override
public List<EmployeeDto> getEmployees() {
Iterable<Employee> employeesIterable = employeeRepository.findAll();
List<Employee> employees = StreamSupport.stream(employeesIterable.spliterator(), false).toList();
return employees.stream().map((emp) -> EmployeeMapper.mapToEmployeeDto(emp)).collect(Collectors.toList());
}
@Override
public void deleteEmployee(String employeeId) throws EmployeeNotFoundException {
Optional<Employee> employee = employeeRepository.findById(employeeId);
if (employee.isEmpty()) {
throw new EmployeeNotFoundException("Employee with id - " + employeeId + " not found.");
}
employeeRepository.deleteById(employeeId);
}
@Override
public EmployeeDto updateEmployee(EmployeeDto employeeDto) throws EmployeeNotFoundException {
Optional<Employee> retrievedEmployee = employeeRepository.findById(employeeDto.getId());
if (retrievedEmployee.isEmpty()) {
throw new EmployeeNotFoundException("Employee with id - " + employeeDto.getId() + " not found.");
}
Employee employee = retrievedEmployee.get();
employee.setName(employeeDto.getName());
employee.setDepartment(employeeDto.getDepartment());
Employee createdEmployee = employeeRepository.save(employee);
return EmployeeMapper.mapToEmployeeDto(createdEmployee);
}
}
Controller Implementation
The method updateEmployee in EmployeeController is responsible for updating an employee's information.
It takes an EmployeeDto object as a parameter, which contains the updated information for the employee.
Inside the method, it calls the updateEmployee method of the employeeService to update the
employee's information. The updated EmployeeDto object is then returned.
Finally, the method creates a new ResponseEntity object using the updated EmployeeDto
and specifies the HTTP status code as HttpStatus.OK. This ResponseEntity object is returned as the response.
package com.javainuse.bootelastisearchcrud.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.javainuse.bootelastisearchcrud.dto.EmployeeDto;
import com.javainuse.bootelastisearchcrud.exception.EmployeeNotFoundException;
import com.javainuse.bootelastisearchcrud.service.EmployeeService;
@RestController
public class EmployeeController {
@Autowired
private EmployeeService employeeService;
@PostMapping(value = "/employee")
public ResponseEntity<EmployeeDto> createEmployee(@RequestBody EmployeeDto employeeDto) {
EmployeeDto createdEmployee = employeeService.createEmployee(employeeDto);
return new ResponseEntity<>(createdEmployee, HttpStatus.CREATED);
}
@GetMapping(value = "/employee/{employeeId}")
public ResponseEntity<EmployeeDto> getEmployeeById(@PathVariable("employeeId") String employeeId)
throws EmployeeNotFoundException {
try {
EmployeeDto employee = employeeService.getEmployeeById(employeeId);
return new ResponseEntity<>(employee, HttpStatus.OK);
} catch (EmployeeNotFoundException employeeNotFoundException) {
throw employeeNotFoundException;
}
}
@GetMapping(value = "/employees")
public ResponseEntity<List<EmployeeDto>> getEmployees() {
List<EmployeeDto> employees = employeeService.getEmployees();
return new ResponseEntity<>(employees, HttpStatus.OK);
}
@DeleteMapping(value = "/employee/{employeeId}")
public ResponseEntity<HttpStatus> deleteEmployees(@PathVariable("employeeId") String employeeId)
throws EmployeeNotFoundException {
employeeService.deleteEmployee(employeeId);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
@PutMapping(value = "/employee")
public ResponseEntity<EmployeeDto> updateEmployee(@RequestBody EmployeeDto employeeDto)
throws EmployeeNotFoundException {
EmployeeDto createdEmployee = employeeService.updateEmployee(employeeDto);
return new ResponseEntity<>(createdEmployee, HttpStatus.OK);
}
}
Test
Let us now test the update employee by id.
Download Source Code
Download it -
Spring Boot 3 + Elasticsearch CRUD Example