Spring Boot + Spring Data Elasticsearch
Introduction#
Spring Data Elasticsearch is a Spring Data implementation for Elasticsearch which provides integration with the Elasticsearch search engine.
Spring Boot and Spring Data Elasticsearch integration
In this example we are going to implement spring-data-elasticsearch project to store POJO in elasticsearch. We will see a sample maven project which does the followings:
- Insert a
Greeting(id, username, message)
item on elasticsearch. - Get All Greeting items which have been inserted.
- Update a Greeting item.
- Delete a Greeting item.
- Get a Greeting item by id.
- Get all Greeting items by username.
Spring boot and spring data elasticsearch integration
In this example we are going to see a maven based spring boot application which integrates spring-data-elasticsearch. Here, we will do the followings and see the respective code segments.
- Insert a
Greeting(id, username, message)
item on elasticsearch. - Get all items from elasticsearch
- Update a specific item.
- Delete a specific item.
- Get a specific item by id.
- Get a specific item by username.
Project configuration file (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springdataes</groupId>
<artifactId>springdataes</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
We will use Spring Boot of version 1.5.6.RELEASE
and Spring Data Elasticsearch of that respective version. For this project, we need to run elasticsearch-2.4.5 to test our apis.
Properties file
We will put the project properties file (named applications.properties
) in resources
folder which contains:
elasticsearch.clustername = elasticsearch
elasticsearch.host = localhost
elasticsearch.port = 9300
We will use the default cluster name, host and port. By default, 9300
port is used as transport port and 9200
port is known as http port. To see the default cluster name hit https://localhost:9200/.
Main Class(Application.java)
package org.springdataes;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String []args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication
is a combination of @Configuration
, @EnableAutoConfiguration
, @EnableWebMvc
and @ComponentScan
annotations. The main()
method uses Spring Boot’s SpringApplication.run()
method to launch an application. There we don’t need any xml configuration, this application is pure java spring application.
Elasticsearch Configuration Class(ElasticsearchConfig.java)
package org.springdataes.config;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import java.net.InetAddress;
@Configuration
@PropertySource(value = "classpath:applications.properties")
@EnableElasticsearchRepositories(basePackages = "org.springdataes.dao")
public class ElasticsearchConfig {
@Value("${elasticsearch.host}")
private String EsHost;
@Value("${elasticsearch.port}")
private int EsPort;
@Value("${elasticsearch.clustername}")
private String EsClusterName;
@Bean
public Client client() throws Exception {
Settings esSettings = Settings.settingsBuilder()
.put("cluster.name", EsClusterName)
.build();
return TransportClient.builder()
.settings(esSettings)
.build()
.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(EsHost), EsPort));
}
@Bean
public ElasticsearchOperations elasticsearchTemplate() throws Exception {
return new ElasticsearchTemplate(client());
}
}
ElasticsearchConfig
class configures elasticsearch to this project and make a connection with elasticsearch. Here, @PropertySource
is used to read the application.properties
file where we store the cluster name, elasticsearch host and port. @EnableElasticsearchRepositories
is used to enable Elasticsearch repositories that Will scan the packages of the annotated configuration class for Spring Data repositories by default. @Value
is used here for reading the properties from the application.properties
file.
The Client()
method creates a transport connection with elasticsearch.
The configuration above sets up an Embedded Elasticsearch Server which is used by the ElasticsearchTemplate
. The ElasticsearchTemplate
bean uses the Elasticsearch Client
and provides a custom layer for manipulating data in Elasticsearch.
Model Class(Greeting.java)
package org.springdataes.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import java.io.Serializable;
@Document(indexName = "index", type = "greetings")
public class Greeting implements Serializable{
@Id
private String id;
private String username;
private String message;
public Greeting() {
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Here we have annotated our Greeting
data objects with a @Document annotation that we can also use to determine index settings like name, numbers of shards or number of replicas. One of the attributes of the class needs to be an id
, either by annotating it with @Id
or using one of the automatically found names id
or documentId
. Here, id
field value will be auto-generated, if we don’t set any value of id
field.
Elasticsearch Repository Class(GreetingRepository.class)
package org.springdataes.dao;
import org.springdataes.model.Greeting;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import java.util.List;
public interface GreetingRepository extends ElasticsearchRepository<Greeting, String> {
List<Greeting> findByUsername(String username);
}
Here, We have extended ElasticsearchRepository
which provide us many of apis that we don’t need to define externally. This is the base repository class for elasticsearch
based domain classes. Since it extends Spring
based repository classes, we get the benefit of avoiding boilerplate code required to implement data access layers for various persistence stores.
Here we have declared a method findByUsername(String username)
which will convert to a match query that matches with username with the username
field of Greeting
objects and returns the list of results.
Services(GreetingService.java)
package org.springdataes.service;
import org.springdataes.model.Greeting;
import java.util.List;
public interface GreetingService {
List<Greeting> getAll();
Greeting findOne(String id);
Greeting create(Greeting greeting);
Greeting update(Greeting greeting);
List<Greeting> getGreetingByUsername(String username);
void delete(String id);
}
Service Bean(GreetingServiceBean.java)
package org.springdataes.service;
import com.google.common.collect.Lists;
import org.springdataes.dao.GreetingRepository;
import org.springdataes.model.Greeting;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class GreetingServiceBean implements GreetingService {
@Autowired
private GreetingRepository repository;
@Override
public List<Greeting> getAll() {
return Lists.newArrayList(repository.findAll());
}
@Override
public Greeting findOne(String id) {
return repository.findOne(id);
}
@Override
public Greeting create(Greeting greeting) {
return repository.save(greeting);
}
@Override
public Greeting update(Greeting greeting) {
Greeting persitedGreeting = repository.findOne(greeting.getId());
if(persitedGreeting == null) {
return null;
}
return repository.save(greeting);
}
@Override
public List<Greeting> getGreetingByUsername(String username) {
return repository.findByUsername(username);
}
@Override
public void delete(String id) {
repository.delete(id);
}
}
In above class, we have @Autowired
the GreetingRepository
. We can simply call the CRUDRepository
methods and the method we have declared in repository class with the GreetingRepository
object.
In getAll()
method, you may find a line Lists.newArrayList(repository.findAll())
. We have done this to convert repository.findAll()
to List<>
item as it returns a Iterable
List.
Controller Class(GreetingController.java)
package org.springdataes.controller;
import org.springdataes.model.Greeting;
import org.springdataes.service.GreetingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api")
public class GreetingController {
@Autowired
private GreetingService greetingService;
@ResponseBody
@RequestMapping(value = "/greetings", method = RequestMethod.GET)
public ResponseEntity<List<Greeting>> getAll() {
return new ResponseEntity<List<Greeting>>(greetingService.getAll(), HttpStatus.OK);
}
@ResponseBody
@RequestMapping(value = "/greetings", method = RequestMethod.POST)
public ResponseEntity<Greeting> insertGreeting(@RequestBody Greeting greeting) {
return new ResponseEntity<Greeting>(greetingService.create(greeting), HttpStatus.CREATED);
}
@ResponseBody
@RequestMapping(value = "/greetings", method = RequestMethod.PUT)
public ResponseEntity<Greeting> updateGreeting(@RequestBody Greeting greeting) {
return new ResponseEntity<Greeting>(greetingService.update(greeting), HttpStatus.MOVED_PERMANENTLY);
}
@ResponseBody
@RequestMapping(value = "/greetings/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Greeting> deleteGreeting(@PathVariable("id") String idd) {
greetingService.delete(idd);
return new ResponseEntity<Greeting>(HttpStatus.NO_CONTENT);
}
@ResponseBody
@RequestMapping(value = "/greetings{id}", method = RequestMethod.POST)
public ResponseEntity<Greeting> getOne(@PathVariable("id") String idd) {
return new ResponseEntity<Greeting>(greetingService.findOne(idd), HttpStatus.OK);
}
@ResponseBody
@RequestMapping(value = "/greetings/{name}", method = RequestMethod.GET)
public ResponseEntity<List<Greeting>> getByUserName(@PathVariable("name") String name) {
return new ResponseEntity<List<Greeting>>(greetingService.getGreetingByUsername(name), HttpStatus.OK);
}
}
Build
To build this maven application run
mvn clean install
Above command first remove all the files in the target
folder and build the project. After building the project we will get the executable .jar file which is named springdataes-1.0-SNAPSHOT.jar
. We can run the main class(Application.java
) to start the process or simply executing the above jar by typing:
java -jar springdataes-1.0-SNAPSHOT.jar
Checking the APIs
For inserting a Greeting item in elasticsearch, execute the below command
curl -H "Content-Type: application/json" -X POST -d '{"username":"sunkuet02","message": "this is a message"}' https://localhost:8080/api/greetings
You should get the below result like
{"id":"AV2ddRxBcuirs1TrVgHH","username":"sunkuet02","message":"this is a message"}
You can also check the get api by executing:
curl -H "Content-Type: application/json" -X GET https://localhost:8080/api/greetings
You should get
[{"id":"AV2ddRxBcuirs1TrVgHH","username":"sunkuet02","message":"this is a message"}]
You can check other apis by following the above processes.
Official Documentations: