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

下方的分页组件代码,均为自己项目自定义,就不分享了,大家可以参考具体实现即可。

  1. 添加 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>
  1. 增加配置
spring:
  elasticsearch:
    rest:
      uris: http://localhost:9200
      username: elastic
      password: 123456
      connection-timeout: 5000
      read-timeout: 5000
  1. 创建 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;

}
  1. 创建 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);
}
  1. 创建分页列表与局部更新公共服务
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;
    }
}
  1. 创建服务并继承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;
    }
}
  1. 在启动类加上注解
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);
    }

}
  1. 自定义 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 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!