Building a Spring Boot CRUD Application with Elasticsearch Integration

D2Y MVN
4 min readOct 20, 2024

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 named customers.

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, while deleteCustomer() 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!

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

D2Y MVN
D2Y MVN

Written by D2Y MVN

Lets make a plane and take a risk!

No responses yet

Write a response