Tuesday, August 9, 2022

Spring Boot2 + Reactive Spring Data + Reactive Elasticsearch + CRUD

Spring Boot + Reactive Spring Data + Reactive Elasticsearch example

Elasticsearch: 

By official documentation Elasticsearch is a distributed, free and open search and analytics engine for all types of data, including textual, numerical, geospatial, structured, and unstructured. Elasticsearch is built on Apache Lucene and was first released in 2010 by Elasticsearch N.V. (now known as Elastic).

Technology

  • Spring Boot 2.7.2
  • Java 17 (Zulu)
  • Docker
  • Maven 
  • Elasticsearch 7.17.5
  • IntelliJ IDEA 
  • Postman

Project Structure


























Configuration Spring Boot project 


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 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.7.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.henry</groupId>
<artifactId>SpringDataElasticsearch</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>SpringDataElasticsearch</name>
<description>Integration SpringData + Elasticsearch</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-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>


application.yml: Configuration reactive elasticsearch 


server:
port: 9000
servlet:
context-path: /
spring:
data:
elasticsearch:
client:
reactive:
endpoints: localhost:9200
elasticsearch:
rest:
uris: http://localhost:9200

Model


package com.henry.SpringDataElasticsearch.model;

import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.annotation.Id;

@Document(indexName = "users")
public class User {

@Id
private Long id;
private String name;
private String lastName;

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

@Override
public String toString() {
return "Users{" +
"id=" + id +
", name='" + name + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
}

Repository


package com.henry.SpringDataElasticsearch.repository;

import com.henry.SpringDataElasticsearch.model.User;
import org.springframework.data.repository.reactive.ReactiveCrudRepository;
import org.springframework.stereotype.Repository;
import reactor.core.publisher.Flux;

@Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {

Flux<User> findByName(String name);
}

Service


package com.henry.SpringDataElasticsearch.service;

import com.henry.SpringDataElasticsearch.model.User;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface UserService {
Mono<User> save(User user);

Mono<User> update(Long id, User user);

Mono<Void> delete(Long id);

Mono<User> findOne(Long id);

Flux<User> findAll();

Flux<User> findByName(String name);
}

ServiceImpl


package com.henry.SpringDataElasticsearch.service.impl;

import com.henry.SpringDataElasticsearch.model.User;
import com.henry.SpringDataElasticsearch.repository.UserRepository;
import com.henry.SpringDataElasticsearch.service.UserService;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Service
public class UserServiceImpl implements UserService {

private final UserRepository userRepository;

public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public Mono<User> save(User user) {
return userRepository.save(user);
}

@Override
public Mono<User> update(Long id, User user) {
return userRepository.findById(id)
.flatMap(u -> {
u.setName(user.getName());
u.setLastName(user.getLastName());
return userRepository.save(u);
});
}

@Override
public Mono<Void> delete(Long id) {
var del = userRepository.deleteById(id);
return del;
}

@Override
public Mono<User> findOne(Long id) {
return userRepository.findById(id);
}

@Override
public Flux<User> findAll() {
return userRepository.findAll();
}

@Override
public Flux<User> findByName(String name) {
return userRepository.findByName(name);
}
}

Controller


package com.henry.SpringDataElasticsearch.controller;

import com.henry.SpringDataElasticsearch.model.User;
import com.henry.SpringDataElasticsearch.service.UserService;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@RestController
@RequestMapping("/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
this.userService = userService;
}

@PostMapping("/save")
public Mono<User> add(@RequestBody User user) {
return userService.save(user);
}

@PutMapping("/update/{id}")
public Mono<User> update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}

@GetMapping("/findOne/{id}")
public Mono<User> findOne(@PathVariable Long id) {
return userService.findOne(id);
}

@GetMapping("/all")
public Flux<User> findAll() {
return userService.findAll();
}

@GetMapping("/findByName/{name}")
public Flux<User> findByName(@PathVariable String name) {
return userService.findByName(name);
}

@DeleteMapping("/delete/{id}")
public Mono<Void> delete(@PathVariable Long id) {
return userService.delete(id);
}

}

Downloading and installing Elasticsearch


docker run -d --name elasticsearch -p 9200:9200 -e "discovery.type=single-node" elasticsearch:7.17.5


Run & Test


Run Spring Boot application with command: mvn spring-boot:run. by console, IntelliJ etc.


POST
localhost:9000/users/save















PUT
localhost:9000/users/update/1
















GET
localhost:9000/users/all


















localhost:9000/users/findByName/henry
















DELETE
localhost:9000/users/delete/1












Basic examples  retrieves documents in Elasticsearch are represented in JSON format.

localhost:9200/_search?q=Henry





















http://localhost:9200/_search?q=lastName:user




















Source Code


Here on GitHub.






References.

https://www.elastic.co/what-is/elasticsearch
https://www.elastic.co/blog/a-practical-introduction-to-elasticsearch
https://www.springcloud.io/post/2022-03/getting-started-with-spring-webflux/#gsc.tab=0
https://piotrminkowski.com/2019/10/25/reactive-elasticsearch-with-spring-boot/
https://mkyong.com/spring-boot/spring-boot-spring-data-elasticsearch-example/
https://www.youtube.com/watch?v=bYiNlCaaRiI
https://www.youtube.com/watch?v=rfjsaccL_e0
https://www.youtube.com/watch?v=uSFNaYlc5ek

No comments:

Post a Comment

Deploying a Spring Boot Application with Cloud SQL and Cloud Run on GCP

In this post, we'll explore how to provision Cloud SQL instances with different connectivity options using Terraform and then access the...