
Building a Spring Boot CRUD Application with Elasticsearch Integration
In this tutorial, we will walk through building a Spring Boot application that performs CRUD operations for a customer entity, integrates with Elasticsearch for full-text search, and uses Lombok to simplify code. The purpose of this tutorial is to show how to build a modern REST API with Spring Boot, Elasticsearch, and Lombok, complete with clean, maintainable code.
By the end, you’ll have a working CRUD application where you can create, update, retrieve, delete, and search for customers using Elasticsearch’s powerful indexing capabilities.
Prerequisites
Before starting, ensure you have the following installed on your machine:
- Java 11 or higher
- Apache Maven
- Elasticsearch (make sure it’s running)
- An IDE (e.g., IntelliJ IDEA, Eclipse)
Project Setup
First, we will set up the project using Spring Boot. Head over to the Spring Initializr and generate a Maven project with the following dependencies:
- Spring Web
- Spring Data Elasticsearch
- Lombok
- Spring Boot DevTools (optional for live reloading)
1. Configuring Elasticsearch
Ensure your application.properties
is configured to connect with your running Elasticsearch instance. By default, Elasticsearch runs on localhost:9200
.
spring.elasticsearch.rest.uris=http://localhost:9200
2. Creating the Customer Entity
We’ll define the Customer
entity, which represents the data model. We use Lombok to simplify the class by removing boilerplate code such as getters, setters, constructors, etc.
Customer.java:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(indexName = "customers")
public class Customer {
@Id
private String id;
private String firstName;
private String lastName;
private String email;
}
- The
@Data
annotation automatically generates getters and setters. @Document(indexName = "customers")
tells Elasticsearch to create an index namedcustomers
.
3. Defining the Customer Repository
Next, we create the repository interface that extends ElasticsearchRepository
. This interface provides methods for basic CRUD operations.
CustomerRepository.java:
import com.d2y.spring_elasticsearch.models.Customer;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface CustomerRepository extends ElasticsearchRepository<Customer, String> {
}
4. Implementing the Customer Service
The service layer will contain the business logic for handling customer data.
CustomerService.java:
import com.d2y.spring_elasticsearch.models.Customer;
import com.d2y.spring_elasticsearch.repositories.CustomerRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
public Customer saveCustomer(Customer customer) {
return customerRepository.save(customer);
}
public List<Customer> findAllCustomers() {
Pageable pageable = PageRequest.of(0, 10);
Page<Customer> customersPage = customerRepository.findAll(pageable);
return customersPage.getContent();
}
public Optional<Customer> findCustomerById(String id) {
return customerRepository.findById(id);
}
public void deleteCustomer(String id) {
customerRepository.deleteById(id);
}
}
- The
findAllCustomers()
method retrieves a paginated list of customers from Elasticsearch. saveCustomer()
persists a new customer to Elasticsearch, whiledeleteCustomer()
removes it.
5. Creating DTOs for Data Transfer
To keep the API structure clean and secure, we’ll create two DTOs: one for requests and one for responses.
CustomerRequestDto.java:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerRequestDto {
private String firstName;
private String lastName;
private String email;
}
CustomerResponseDto.java:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CustomerResponseDto {
private String id;
private String firstName;
private String lastName;
private String email;
}
6. Building the Customer Controller
We now define the RESTful endpoints in a controller. These endpoints allow us to create, read, update, delete, and list customers.
CustomerController.java:
import com.d2y.spring_elasticsearch.dto.CustomerRequestDto;
import com.d2y.spring_elasticsearch.dto.CustomerResponseDto;
import com.d2y.spring_elasticsearch.models.Customer;
import com.d2y.spring_elasticsearch.services.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/customers")
public class CustomerController {
@Autowired
private CustomerService customerService;
@PostMapping
public ResponseEntity<CustomerResponseDto> createCustomer(@RequestBody CustomerRequestDto customerRequestDto) {
Customer customer = mapToCustomerEntity(customerRequestDto);
Customer savedCustomer = customerService.saveCustomer(customer);
CustomerResponseDto customerResponseDto = mapToCustomerResponseDto(savedCustomer);
return ResponseEntity.ok(customerResponseDto);
}
@GetMapping
public ResponseEntity<List<CustomerResponseDto>> getAllCustomers() {
List<Customer> customers = customerService.findAllCustomers();
List<CustomerResponseDto> customerResponseDtos = customers.stream()
.map(this::mapToCustomerResponseDto)
.collect(Collectors.toList());
return ResponseEntity.ok(customerResponseDtos);
}
@GetMapping("/{id}")
public ResponseEntity<CustomerResponseDto> getCustomerById(@PathVariable String id) {
Optional<Customer> customer = customerService.findCustomerById(id);
if (customer.isPresent()) {
CustomerResponseDto customerResponseDto = mapToCustomerResponseDto(customer.get());
return ResponseEntity.ok(customerResponseDto);
} else {
return ResponseEntity.notFound().build();
}
}
@PutMapping("/{id}")
public ResponseEntity<CustomerResponseDto> updateCustomer(@PathVariable String id, @RequestBody CustomerRequestDto customerDetails) {
Optional<Customer> customer = customerService.findCustomerById(id);
if (customer.isPresent()) {
Customer updatedCustomer = mapToCustomerEntity(customerDetails);
updatedCustomer.setId(id); // Set the ID for the update
Customer savedCustomer = customerService.saveCustomer(updatedCustomer);
CustomerResponseDto customerResponseDto = mapToCustomerResponseDto(savedCustomer);
return ResponseEntity.ok(customerResponseDto);
} else {
return ResponseEntity.notFound().build();
}
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteCustomer(@PathVariable String id) {
customerService.deleteCustomer(id);
return ResponseEntity.noContent().build();
}
private Customer mapToCustomerEntity(CustomerRequestDto dto) {
return new Customer(null, dto.getFirstName(), dto.getLastName(), dto.getEmail());
}
private CustomerResponseDto mapToCustomerResponseDto(Customer customer) {
return new CustomerResponseDto(customer.getId(), customer.getFirstName(), customer.getLastName(), customer.getEmail());
}
}
- The
@PostMapping
and@PutMapping
handle the creation and updating of customers. - The
@GetMapping
allows you to retrieve customer information, including fetching by ID and getting a list of customers. - The
@DeleteMapping
is responsible for removing customers from Elasticsearch.
7. Running the Application
Now that all components are in place, you can run the application with:
mvn spring-boot:run
In this tutorial, we’ve built a Spring Boot CRUD application that integrates with Elasticsearch and uses Lombok to keep the code concise and readable. Elasticsearch allows for powerful full-text search, while Lombok helps reduce boilerplate in your model classes. This combination makes for a scalable, efficient, and modern Spring Boot application.
You can check out the complete code for this project on GitHub.
Happy coding!