1. 程式人生 > >Springboot中整合Spring data elasticsearch,實現相關CRUD介面

Springboot中整合Spring data elasticsearch,實現相關CRUD介面

專案環境:

JDK:1.8

SringBoot:2.1.0.RELEASE

Gardle:gradle-4.10.2

ElasticSearch:elasticsearch-6.2.4

Spring-data-elasticsearch:spring-data-elasticsearch:3.1.2.RELEASE

正文:

Java與ElasticSearch連線的兩種方式:(1)使用Transport與ElasticSearch建立連線

                                                              (2)使用SpringDataElasticSearch連線連線

兩種方式的優缺點:(1)優點:脫離框架,整合過程中不需要考慮與Spring的版本相容問題,容易整合

                                          缺點:使用原生API操作ES,程式碼量大,撰寫困難

                                (2)優點:將原生API進行封裝,提供了ElasticsearchRepository,操作ES非常簡單,與JPA同理

                                         缺點:出生於Spring家族,與SpringBoot,SpringData版本容易衝突

本教程使用Springboot+SpringDataElastic+gradle整合

專案依賴如下:

dependencies {

    compile('org.springframework.boot:spring-boot-starter')

    // 使用SpringDataElasticSearch只需要新增一處依賴即用
    compile('org.springframework.boot:spring-boot-starter-data-elasticsearch')

    testCompile('org.springframework.boot:spring-boot-starter-test')

    // 使用lombok提供Getter與Setter,實體類只需寫欄位,加註釋,外部類即可以通過構造器呼叫
    annotationProcessor 'org.projectlombok:lombok:1.18.2'
    compileOnly 'org.projectlombok:lombok:1.18.2'
    testAnnotationProcessor 'org.projectlombok:lombok:1.18.2'
    testCompileOnly 'org.projectlombok:lombok:1.18.2'

}

Java(實體-業務)程式碼如下:

  實體類pojo:

import lombok.Getter;
import lombok.Setter;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import javax.persistence.Id;
import java.io.Serializable;

@Getter
@Setter
@Document(indexName = "poms", type = "content")
public class ESDocument implements Serializable {

    @Id
    private String id;

    @Field(analyzer = "ik_smart", searchAnalyzer = "ik_smart")
    private String name;

    private String projectId;

}

注:實體類需要新增@Document,專案啟動會在ES中自動建立index與type,需要中文分詞的欄位使用@Field指定分詞器名稱

(前提是ES服務安裝了ik分詞器)

持久化dao Interface:

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;


@Repository
public interface DocumentSearchRepository extends ElasticsearchRepository<ESDocument, 
                                                                          String>   {
}

ElasticsearchRepository介面與JpaRepository同理,使用時可根據方法名稱自動生成 ES Query語句,簡單操作可使用該介面提供的方法(包括page,sort),簡單的CRUD操作直接使用DocumentSearchRepository例項物件就可調出方法,

業務service Interface:

public interface DocumentSearchService {


    ESDocument getDocumentById(String id) throws WSException;


    void deleteDocumentById(String id);


    /**
     * build add documents for the index
     *
     * @param ESDocuments added documents
     */
    void saveDocument(List<ESDocument> ESDocuments);


    /**
     * get document list by name and id order by orderField parameter
     *
     * @param name       queried name
     * @param projectId  contained project id
     * @param orderField order filed  name
     * @return document list
     * @throws GTException
     */
    List<ESDocument> getDocumentsByNameOrderByCreateOn(String name, 
                                                       String projectId,                     
                                                       String orderField) 
                                                       throws WSException;


}

業務service Implments class:

@Service
public class DocumentSearchServiceImpl implements DocumentSearchService {

    @Autowired
    private DocumentSearchRepository documentSearchRepository;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Override
    public ESDocument getDocumentById(String id) throws WSException {
        ESDocument country = documentSearchRepository.findById(id)
                                             .orElseThrow(() -> 
                                             new GTException(HttpStatus.NOT_FOUND, 
                                             ErrorCode.ES_REPOSITORY_DOCUMENT_NOT_EXISIS, 
                                             id));
        return country;
    }


    @Override
    public void deleteDocumentById(String id) {
        documentSearchRepository.deleteById(id);
    }


    /**
     * build add documents for the index
     *
     * @param ESDocuments added documents
     */
    @Override
    public void saveDocument(List<ESDocument> ESDocuments) {
        documentSearchRepository.saveAll(ESDocuments);
    }


    /**
     * get document list by name and id order by orderField parameter
     *
     * @param name       queried name
     * @param projectId  contained project id
     * @param orderField order filed  name
     * @return document list
     * @throws WSException
     */
    @Override
    public List<ESDocument> getDocumentsByNameOrderByCreateOn(String name, String 
                                 projectId, String orderField) throws WSException {
        List<ESDocument> ESDocuments;
        try {
            SearchQuery searchQuery = new NativeSearchQueryBuilder()
                    .withQuery(matchQuery("name", name))
                    .withFilter(matchPhraseQuery("projectId", projectId))
                    .withSort(SortBuilders.scoreSort().order(SortOrder.DESC))
                    .withSort(new FieldSortBuilder(orderField).order(SortOrder.DESC))
                    .build();
            ESDocuments = elasticsearchTemplate.queryForList(searchQuery, 
                                                             ESDocument.class);
        } catch (Exception e) {
            throw new WSException(HttpStatus.NOT_MODIFIED, 
                                  ErrorCode.ES_REPOSITORY_FIELD_NOT_EXISTS, e);
        }
        return ESDocuments;
    }
}

簡單的查詢和條件查詢可以直接使用ElasticsearchRepository提供的介面,如果需要複雜的條件組合(模糊查詢,完全匹配查 詢,分頁,排序)使用ElasticSearchTemplate例項,它一般最常用的方法是queryForList(SearchQuery query, Class<T> clazz),

將查詢條件拼接到一個SearchQuery中。這個例項不需要再任何地方建立,當專案初始化的時候已經在IOC容器中建立完成了。

使用只需DI注入即可(詳情可參考DocumentSearchServiceImpl類的getDocumentsByNameOrderByCreateOn方法)。

另外是我的整合公司專案遇到的一個坑:由於公司持久化框架也是Spring-data-Jpa,內部使用的都是CrudRepository完成CRUD,在整合的時候兩個一直衝突。建議將有關ES的Interface,class與JPA的Interface,class放在不同的根目錄,實體類不能引用同一個,需要建立兩份實體類,ES與JPA各自用各自的,否則專案跑不起來!!!