java與es8實戰之四:SpringBoot應用中操作es8(無安全檢查)

歡迎訪問我的GitHub

這裏分類和彙總了欣宸的全部原創(含配套源碼):https://github.com/zq2599/blog_demos

本篇概覽

  • 本篇是《java與es8實戰》系列的第四篇,系列文章寫到現在,連個HelloWorld都沒運行起來,實在說不過去了...
  • 因此,本篇總體目標明確:實戰在SpringBoot應用中操作elasticsearch8
  • 爲了降低難度,本篇部署的elasticsearch8未設置安全檢查,無需證書、賬號、密碼,只要連接到es的IP和端口就能執行操作
  • 總體目標可以拆解爲兩個子任務
  1. 在SpringBoot中連接elasticsearch8
  2. 在SpringBoot中使用elasticsearch8官方的Java API Client
  • 接下來直接開始

部署elasticsearch集羣(無安全檢查)

Java應用連接elasticsearch的核心套路

  • 不論是直連,還是帶安全檢查的連接,亦或是與SpringBoot的集成使之更方便易用,都緊緊圍繞着一個不變的核心套路,該套路由兩部分組成,掌握了它們就能在各種條件下成功連接es
  1. 首先,是builder pattern,連接es有關的代碼,各種對象都是其builder對象的build方法創建的,建議您提前閱讀《java與es8實戰之一》一文,看完後,滿屏的builder代碼可以從醜變成美...
  2. 其次,就是java應用能向es發請求的關鍵:ElasticsearchClient對象,該對象的創建是有套路的,如下圖,先創建RestClient,再基於RestClient創建ElasticsearchTransport,最後基於ElasticsearchTransport創建ElasticsearchClient,這是個固定的套路,咱們後面的操作都是基於此的,可能會加一點東西,但不會改變流程和圖中的對象
    在這裏插入圖片描述
  • 準備完畢,開始寫代碼

新建子工程

  • 爲了便於管理依賴庫版本和源碼,《java與es8實戰》系列的所有代碼都以子工程的形式存放在父工程elasticsearch-tutorials

  • 《java與es8實戰之二:實戰前的準備工作》一文說明了創建父工程的詳細過程

  • 在父工程elasticsearch-tutorials中新建名爲basic-crud的子工程,其pom.xml內容如下

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <!-- 請改爲自己項目的parent座標 -->
    <parent>
        <artifactId>elasticsearch-tutorials</artifactId>
        <groupId>com.bolingcavalry</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <!-- 請改爲自己項目的artifactId -->
    <artifactId>basic-crud</artifactId>
    <packaging>jar</packaging>
    <!-- 請改爲自己項目的name -->
    <name>basic-crud</name>
    <url>https://github.com/zq2599</url>

    <!--不用spring-boot-starter-parent作爲parent時的配置-->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>

                <version>${springboot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- 不加這個,configuration類中,IDEA總會添加一些提示 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</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>

            <!-- exclude junit 4 -->
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>

        </dependency>

        <!-- junit 5 -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- elasticsearch引入依賴  start -->
        <dependency>
            <groupId>co.elastic.clients</groupId>
            <artifactId>elasticsearch-java</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>

        <!-- 使用spring boot Maven插件時需要添加該依賴 -->
        <dependency>
            <groupId>jakarta.json</groupId>
            <artifactId>jakarta.json-api</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 需要此插件,在執行mvn test命令時纔會執行單元測試 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M4</version>
                <configuration>
                    <skipTests>false</skipTests>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>

        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
            </resource>
        </resources>
    </build>
</project>

編碼:配置文件

  • 先準備好配置文件application.yml,內容如下,很簡單,只有es的地址信息
elasticsearch:
  # 多個IP逗號隔開
  hosts: 127.0.0.1:9200

編碼:配置類

  • 首先把啓動類寫好,平平無奇的啓動類BasicCrudApplication.java
@SpringBootApplication
public class BasicCrudApplication {
    public static void main(String[] args) {
        SpringApplication.run(BasicCrudApplication.class, args);
    }
}
  • 然後是配置類ClientConfig.java,這是本篇的關鍵,操作ES所需的ElasticsearchClient實例如何創建,ES的IP地址如何傳入,全部寫在這裏了
package com.bolingcavalry.basic.config;

import co.elastic.clients.elasticsearch.ElasticsearchAsyncClient;
import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.Setter;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

@ConfigurationProperties(prefix = "elasticsearch") //配置的前綴
@Configuration
public class ClientConfig {

    @Setter
    private String hosts;

    /**
     * 解析配置的字符串,轉爲HttpHost對象數組
     * @return
     */
    private HttpHost[] toHttpHost() {
        if (!StringUtils.hasLength(hosts)) {
            throw new RuntimeException("invalid elasticsearch configuration");
        }

        String[] hostArray = hosts.split(",");
        HttpHost[] httpHosts = new HttpHost[hostArray.length];
        HttpHost httpHost;
        for (int i = 0; i < hostArray.length; i++) {
            String[] strings = hostArray[i].split(":");
            httpHost = new HttpHost(strings[0], Integer.parseInt(strings[1]), "http");
            httpHosts[i] = httpHost;
        }

        return httpHosts;
    }

    @Bean
    public ElasticsearchClient elasticsearchClient() {
        HttpHost[] httpHosts = toHttpHost();
        RestClient restClient = RestClient.builder(httpHosts).build();
        RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchClient(transport);
    }

    @Bean
    public ElasticsearchAsyncClient elasticsearchAsyncClient() {
        HttpHost[] httpHosts = toHttpHost();
        RestClient restClient = RestClient.builder(httpHosts).build();
        RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
        return new ElasticsearchAsyncClient(transport);
    }
}
  • 從上面的代碼可以看出,配置類已經向Spring容器註冊了ElasticsearchClient實例,後面的業務都可以使用此實例來操作ES

編碼:服務類

  • 本篇只是爲了演示SpringBoot應用如何連接和操作ES,還不會深入ES操作的細節,因此只對索引做一些基本操作即可

  • 先寫一個接口IndexService.java,裏面定義了多個索引操作的方法

package com.bolingcavalry.basic.service;

import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.util.ObjectBuilder;

import java.io.IOException;
import java.util.function.Function;

public interface IndexService {

    /**
     * 新建指定名稱的索引
     * @param name
     * @throws IOException
     */
    void addIndex(String name) throws IOException;

    /**
     * 檢查指定名稱的索引是否存在
     * @param name
     * @return
     * @throws IOException
     */
    boolean indexExists(String name) throws IOException;

    /**
     * 刪除指定索引
     * @param name
     * @throws IOException
     */
    void delIndex(String name) throws IOException;

    /**
     * 創建索引,指定setting和mapping
     * @param name 索引名稱
     * @param settingFn 索引參數
     * @param mappingFn 索引結構
     * @throws IOException
     */
    void create(String name,
                Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn,
                Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn) throws IOException;
}
  • 然後接口的實現,可見所有操作都是在調用ElasticsearchClient實例的API
package com.bolingcavalry.basic.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.util.ObjectBuilder;
import com.bolingcavalry.basic.service.IndexService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.function.Function;

@Service
public class IndexServiceImpl implements IndexService {

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Override
    public void addIndex(String name) throws IOException {
        ApplicationContext applicationContext;
        elasticsearchClient.indices().create(c -> c.index(name));
    }

    @Override
    public boolean indexExists(String name) throws IOException {
        ApplicationContext a;
        return elasticsearchClient.indices().exists(b -> b.index(name)).value();
    }

    @Override
    public void delIndex(String name) throws IOException {
        elasticsearchClient.indices().delete(c -> c.index(name));
    }

    @Override
    public void create(String name,
                       Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn,
                       Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn) throws IOException {
       elasticsearchClient
               .indices()
               .create(c -> c
                       .index(name)
                       .settings(settingFn)
                       .mappings(mappingFn)
               );
    }
}
  • 以上就是本篇的功能代碼了,連接ES在其上進行索引相關操作

編碼:單元測試

  • 爲了驗證上述代碼是否生效,接下來寫一個單元測試類IndexServiceTest.java,可以重點關注createIndex方法,裏面演示了Builder pattern構建參數的詳細步驟
package com.bolingcavalry.basic.service;

import co.elastic.clients.elasticsearch._types.mapping.Property;
import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
import co.elastic.clients.elasticsearch.indices.IndexSettings;
import co.elastic.clients.util.ObjectBuilder;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.function.Function;

@SpringBootTest
class IndexServiceTest {

    @Autowired
    IndexService indexService;

    @Test
    void addIndex() throws Exception {
        String indexName = "test_index";

        Assertions.assertFalse(indexService.indexExists(indexName));
        indexService.addIndex(indexName);
        Assertions.assertTrue(indexService.indexExists(indexName));
        indexService.delIndex(indexName);
        Assertions.assertFalse(indexService.indexExists(indexName));
    }

    @Test
    void indexExists() throws Exception {
        indexService.indexExists("a");
    }

    @Test
    void createIndex() throws Exception {
        // 索引名
        String indexName = "product002";

        // 構建setting時,builder用到的lambda
        Function<IndexSettings.Builder, ObjectBuilder<IndexSettings>> settingFn = sBuilder -> sBuilder
                .index(iBuilder -> iBuilder
                        // 三個分片
                        .numberOfShards("3")
                        // 一個副本
                        .numberOfReplicas("1")
                );

        // 新的索引有三個字段,每個字段都有自己的property,這裏依次創建
        Property keywordProperty = Property.of(pBuilder -> pBuilder.keyword(kBuilder -> kBuilder.ignoreAbove(256)));
        Property textProperty = Property.of(pBuilder -> pBuilder.text(tBuilder -> tBuilder));
        Property integerProperty = Property.of(pBuilder -> pBuilder.integer(iBuilder -> iBuilder));

        // // 構建mapping時,builder用到的lambda
        Function<TypeMapping.Builder, ObjectBuilder<TypeMapping>> mappingFn = mBuilder -> mBuilder
                .properties("name", keywordProperty)
                .properties("description", textProperty)
                .properties("price", integerProperty);

        // 創建索引,並且指定了setting和mapping
        indexService.create(indexName, settingFn, mappingFn);
    }
}
  • 確保不做安全檢查的ES集羣運行正常,再執行單元測試,如下圖,順利通過,證明所有對ES的操作都符合預期
    在這裏插入圖片描述
  • 再用eshead觀察product002索引的情況,如下圖,三個分片,一個副本,與代碼中設置的一致
    在這裏插入圖片描述
  • 至此最簡單的連接和操作ES實戰已經完成,希望本篇能給您一些參考,助您順利完成基本操作

是不是線程安全的

源碼下載

名稱 鏈接 備註
項目主頁 https://github.com/zq2599/blog_demos 該項目在GitHub上的主頁
git倉庫地址(https) https://github.com/zq2599/blog_demos.git 該項目源碼的倉庫地址,https協議
git倉庫地址(ssh) [email protected]:zq2599/blog_demos.git 該項目源碼的倉庫地址,ssh協議
  • 這個git項目中有多個文件夾,本次實戰的源碼在elasticsearch-tutorials文件夾下,如下圖紅框
    在這裏插入圖片描述
  • elasticsearch-tutorials是個父工程,裏面有多個module,本篇實戰的module是basic-crud,如下圖紅框
    在這裏插入圖片描述

歡迎關注博客園:程序員欣宸

學習路上,你不孤單,欣宸原創一路相伴...

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章