Spring Boot 接入 Elasticsearch 分享(少走弯路)
翻了很多资料,包括官方文档,发现很多地方其实新手比较困难,有点无从下手,这个文档,把自己踩过的坑,补全,帮助新手更快入门。希望新手入门后,也能逐渐研究文档,提升自己的见解~
文档:
docs.spring.io/spring-data/elastic...
注意:
因 SpringBoot
Repository
多条件查询需要单独声明,无法使用链式调用,所以需要使用 ElasticsearchRestTemplate
实现,故提取了 BaseService
服务
如果不使用自定义 mapping
的话,entity
时间类型,需要使用 LocalDateTime
类型,才能被 Elasticsearch
感知。在 kibana
& Elasticsearch
内的数据类型才会成为 Date
。否则使用Date
类型时,转换到 Elasticsearch
内为 Long
类型。
相关参考:
docs.spring.io/spring-data/elastic...
下方的分页组件代码,均为自己项目自定义,就不分享了,大家可以参考具体实现即可。
- 添加 pom
<!--引入es的坐标-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.10.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.10.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.5.2</version>
</dependency>
- 增加配置
spring:
elasticsearch:
rest:
uris: http://localhost:9200
username: elastic
password: 123456
connection-timeout: 5000
read-timeout: 5000
- 创建
entity
package com.repository.dao.es;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author Ritin
* @date 2021/7/21 10:23
* @version 1.0
*/
@Data
@Document(indexName = "es_product")
public class EsProduct {
@Id
private Long id;
private Long name;
private BigDecimal price;
private Date createdAt;
private Date updatedAt;
/**
* 时间类型,需要使用 LocalDateTime
*/
private LocalDateTime timestamp;
}
- 创建
repository
package com.repository.dao.es.repository;
import com.dao.es.EsProduct;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/**
* @author Ritin
* @date 2021/7/23 13:38
* @version 1.0
*/
public interface EsProductRepository extends ElasticsearchRepository<EsProduct, Long> {
/**
* 根据 id 查询单条记录
* @param id Long
* @return EsProduct
*/
EsProduct queryByIdEquals(Long id);
}
- 创建分页列表与局部更新公共服务
package com.repository.service.es;
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.data.elasticsearch.core.query.UpdateQuery;
import org.springframework.data.elasticsearch.core.query.UpdateResponse;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @author Ritin
* @date 2021/7/23 09:59
* @version 1.0
*/
@Service
public class BaseService {
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 局部 update
* @param updateObject 更新对象 如 new EsUser
* @param id 更新 id
* @param clazz 更新 DTO
* @return true 为成功,false 为失败,一般情况下是值无变动
*/
public boolean update(Object updateObject, String id, Class<?> clazz) {
// 获取 index
String indexName = elasticsearchRestTemplate.getIndexCoordinatesFor(clazz).getIndexName();
// 写入文档
Document document = Document.parse(JSON.toJSONString(updateObject));
// 更新
UpdateResponse response = elasticsearchRestTemplate.update(
UpdateQuery.builder(id).withDocument(document).build(),
IndexCoordinates.of(indexName)
);
// 判断是否成功
String updateStatus = "UPDATED";
return Objects.equals(updateStatus, response.getResult().toString());
}
/**
* elasticsearch + 自定义 分页组件结合
* 适用于后台列表等需要分页操作
* @param query 查询语句
* @param request 分页类
* @param clazz DTO
* @param <T> DTO
* @return PageResp<T>
*/
public <T> PageResp<T> search(Query query, BasePageReq request, Class<T> clazz) {
// 查询
SearchHits<T> searchHits = elasticsearchRestTemplate.search(query, clazz);
// 转换
List<T> list = new ArrayList<>();
searchHits.stream().iterator().forEachRemaining(searchHit -> list.add(searchHit.getContent()));
// 设置 自定义 分页组件
PageResp<T> response = new PageResp<>();
response.setPages(searchHits.getTotalHits() / PageUtil.handlePageSizeDefault(request));
response.setTotal(searchHits.getTotalHits());
response.setList(list);
// 获取分页默认值,自行实现即可
response.setPageNo(PageUtil.handlePageNoDefault(request));
response.setPageSize(PageUtil.handlePageSizeDefault(request));
return response;
}
/**
* elasticsearch 查询 list
* 适用于导出、统计等无需分页处理操作
* @param query 查询语句
* @param clazz DTO
* @param <T> DTO
* @return PageResp<T>
*/
public <T> List<T> search(Query query, Class<T> clazz) {
// 查询
SearchHits<T> searchHits = elasticsearchRestTemplate.search(query, clazz);
// 转换
List<T> list = new ArrayList<>();
searchHits.stream().iterator().forEachRemaining(searchHit -> list.add(searchHit.getContent()));
return list;
}
}
- 创建服务并继承service
package com.repository.service.es;
import com.repository.dao.es.EsFinance;
import com.repository.dao.es.repository.EsFinanceRepository;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @author Ritin
* @date 2021/7/23 13:40
* @version 1.0
*/
@Service
public class EsFinanceService extends BaseService {
@Autowired
private EsProductRepository esProductRepository;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 获取单条记录
* @param id Long
* @return EsProduct
*/
public EsProduct getById(Long id) {
return esProductRepository.queryByIdEquals(id);
}
/**
* 查询名字为产品1的列表
* @return List<EsProduct>
*/
public List<EsProduct> getList() {
BoolQueryBuilder bqb = QueryBuilders.boolQuery().bqb.must(QueryBuilders.matchPhraseQuery("name", "产品1"));
Query query = new NativeSearchQueryBuilder().withQuery(bqb).build();
return this.search(query, EsFinance.class);
}
public EsProduct create() {
EsProduct esProduct = new EsProduct();
esProduct.setId(1L);
esProduct.setName("产品1");
esProduct.setCreatedAt(new Date());
esProduct.setUpdatedAt(new Date());
esProductRepository.save(esProduct);
return esProduct;
}
}
- 在启动类加上注解
package com.XXXX.admin;
import org.springframework.boot.SpringApplication;
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
@EnableElasticsearchRepositories(basePackages = "com.XXXX")
public class AppApplication {
public static void main(String[] args) {
SpringApplication.run(AppApplication.class, args);
}
}
- 自定义
Bean
package com.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author Ritin
* @version 1.0
* @date 2021/9/15 17:59
*/
@Configuration
public class EsConfiguration {
@Value("${es.index.product}")
private String product;
@Bean
public String product() {
// 甚至可以添加属于自己的自定义索引名字,比如需要按照日期变动的
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd");
return product + simpleDateFormat.format(new Date());
}}
本作品采用《CC 协议》,转载必须注明作者和本文链接