Spring Boot Elasticsearch
Spring Boot Elasticsearch 6
在本文中,我们将设置一个示例Spring boot Elasticsearch应用程序。
我们将使用最新版本的Elasticsearch,即6.1.x。
为了与Elasticsearch搜索引擎进行交互,我们将使用Elasticsearch Rest客户端。
我们不使用Spring Data ElasticSearch,因为它不支持最新的ElasticSearch版本(即6.x)。
设置项目
Maven依赖
我们将为此项目使用Maven构建系统,这是我们使用的依赖项:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
<relativePath
<!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</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-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Elasticsearch Dependencies -->
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.1.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>6.1.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client-sniffer</artifactId>
<version>6.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
确保从Maven Central使用稳定版本的Spring Boot。
Elasticsearch配置
现在,我们将必须在应用程序中配置ElasticSearch。
让我们分为两个部分。
首先,我们将在" application.properties"文件中提供Elasticsearch地址:
spring.data.elasticsearch.cluster-name=elasticsearch spring.data.elasticsearch.cluster-nodes=elasticsearch
我们仅在此处提供了Elasticsearch集群名称和节点名称,而这些实际上是默认值。
现在,是时候在Java配置类中使用这些值了。
package com.theitroad.elasticsearch.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticSearchConfiguration extends AbstractFactoryBean {
private static final Logger LOG = LoggerFactory.getLogger(ElasticSearchConfiguration.class);
@Value("${spring.data.elasticsearch.cluster-nodes}")
private String clusterNodes;
@Value("${spring.data.elasticsearch.cluster-name}")
private String clusterName;
private RestHighLevelClient restHighLevelClient;
@Override
public void destroy() {
try {
if (restHighLevelClient != null) {
restHighLevelClient.close();
}
} catch (final Exception e) {
LOG.error("Error closing ElasticSearch client: ", e);
}
}
@Override
public Class<RestHighLevelClient> getObjectType() {
return RestHighLevelClient.class;
}
@Override
public boolean isSingleton() {
return false;
}
@Override
public RestHighLevelClient createInstance() {
return buildClient();
}
private RestHighLevelClient buildClient() {
try {
restHighLevelClient = new RestHighLevelClient(
RestClient.builder(
new HttpHost("localhost", 9200, "http"),
new HttpHost("localhost", 9201, "http")));
} catch (Exception e) {
LOG.error(e.getMessage());
}
return restHighLevelClient;
}
}
通过这种配置,我们确保了ElasticSearch能够使用Rest Client API与其服务器成功建立连接。
使用应用程序
现在就开始放置应用程序的工作组件。
应用程式模型
我们将以一个简单的模型作为用户:
package com.theitroad.elasticsearch.model;
import com.fasterxml.Hymanson.annotation.JsonInclude;
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Book {
private String id;
private String title;
private String author;
private float price;
//standard setters and getters
}
我们将在应用程序中进行以下功能和数据库交互:
- 取得ID为一本书的书
- 插入一本书
- 更新一本书
- 删除书籍
定义控制器
让我们开始制作控制器:
package com.theitroad.elasticsearch.controller;
import com.theitroad.elasticsearch.model.Book;
import com.theitroad.elasticsearch.dao.BookDao;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/books")
public class BookController {
private BookDao bookDao;
public BookController(BookDao bookDao) {
this.bookDao = bookDao;
}
...
}
我们只是自动装配DAO依赖项,接下来将使用它。
定义API
对于我们提到的功能,我们现在将制作API并访问内部将使用Elasticsearch Rest Client API的DAO依赖项。
取得ID为一本书的书
让我们得到一本ID为的书:
@GetMapping("/{id}")
public Map<String, Object> getBookById(@PathVariable String id){
return bookDao.getBookById(id);
}
插入一本书
现在,让我们现在插入一本书:
@PostMapping
public Book insertBook(@RequestBody Book book) throws Exception {
return bookDao.insertBook(book);
}
更新一本书
我们将在以下片段中更新一本书:
@PutMapping("/{id}")
public Map<String, Object> updateBookById(@RequestBody Book book, @PathVariable String id) {
return bookDao.updateBookById(id, book);
}
删除书
现在,我们已将示例数据添加到数据库中,让我们尝试提取其中的一部分:
@DeleteMapping("/{id}")
public void deleteBookById(@PathVariable String id) {
bookDao.deleteBookById(id);
}
这是Spring Data API为我们提供的便利,但是它也有一些缺点。
当我们还定义了ElasticsearchTemplate版本时,我们将详细说明。
让我们也开始吧。
定义DAO层
现在,我们将实际定义实现这些目标的DAL查询。
定义DAO
我们将从提及我们需要的依赖关系开始:
package com.theitroad.elasticsearch.dao;
import com.theitroad.elasticsearch.model.Book;
import com.fasterxml.Hymanson.core.JsonProcessingException;
import com.fasterxml.Hymanson.databind.ObjectMapper;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@Repository
public class BookDao {
private final String INDEX = "bookdata";
private final String TYPE = "books";
private RestHighLevelClient restHighLevelClient;
private ObjectMapper objectMapper;
public BookDao( ObjectMapper objectMapper, RestHighLevelClient restHighLevelClient) {
this.objectMapper = objectMapper;
this.restHighLevelClient = restHighLevelClient;
}
...
}
插入查询
我们将从在ES中插入一本书开始:
public Book insertBook(Book book){
book.setId(UUID.randomUUID().toString());
Map dataMap = objectMapper.convertValue(book, Map.class);
IndexRequest indexRequest = new IndexRequest(INDEX, TYPE, book.getId())
.source(dataMap);
try {
IndexResponse response = restHighLevelClient.index(indexRequest);
} catch(ElasticsearchException e) {
e.getDetailedMessage();
} catch (java.io.IOException ex){
ex.getLocalizedMessage();
}
return book;
}
显然,在将模型数据插入ES数据库之前,需要将其转换为Map数据结构。
搜索查询
现在,我们将搜索ID为:的图书:
public Map<String, Object> getBookById(String id){
GetRequest getRequest = new GetRequest(INDEX, TYPE, id);
GetResponse getResponse = null;
try {
getResponse = restHighLevelClient.get(getRequest);
} catch (java.io.IOException e){
e.getLocalizedMessage();
}
Map<String, Object> sourceAsMap = getResponse.getSourceAsMap();
return sourceAsMap;
}
在此,要注意的是,数据也作为地图数据结构被搜索并返回。
这是我们需要处理的事情,将数据转换为Map来进行数据库事务。
public Map<String, Object> updateBookById(String id, Book book){
UpdateRequest updateRequest = new UpdateRequest(INDEX, TYPE, id)
.fetchSource(true); //Fetch Object after its update
Map<String, Object> error = new HashMap<>();
error.put("Error", "Unable to update book");
try {
String bookJson = objectMapper.writeValueAsString(book);
updateRequest.doc(bookJson, XContentType.JSON);
UpdateResponse updateResponse = restHighLevelClient.update(updateRequest);
Map<String, Object> sourceAsMap = updateResponse.getGetResult().sourceAsMap();
return sourceAsMap;
}catch (JsonProcessingException e){
e.getMessage();
} catch (java.io.IOException e){
e.getLocalizedMessage();
}
return error;
}
删除资料
现在,我们将搜索具有ID的图书:
public void deleteBookById(String id) {
DeleteRequest deleteRequest = new DeleteRequest(INDEX, TYPE, id);
try {
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest);
} catch (java.io.IOException e){
e.getLocalizedMessage();
}
}
我认为删除对象的查询最简单。
现在,通过运行它来尝试该应用程序。
运行应用程序
我们只需使用一个命令即可运行此应用程序:
mvn spring-boot:run
应用程序运行后,我们可以尝试使用此API保存新书。
在此之前,只需使用以下API确认ES正在运行:
127.0.0.1:9200
我们将收到以下响应:现在,我们使用以下API插入数据:
127.0.0.1:8080/books
我们使用带有JSON的POST请求:
{
"title" : "Java Always",
"author" : "theitroad",
"price" : 99.1
}
我们的回应如下:让我们尝试另一个请求,以获取具有上述ID的图书。
我们将在此API上使用GET请求:
127.0.0.1:8080/books/55c200ff-9674-44aa-8779-a0f3ff925e74

