• 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内容如下

    
    
    <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>
            <artifactId>elasticsearch-tutorialsartifactId>
            <groupId>com.bolingcavalrygroupId>
            <version>1.0-SNAPSHOTversion>
            <relativePath>../pom.xmlrelativePath>
        parent>
        <modelVersion>4.0.0modelVersion>
        
        <artifactId>basic-crudartifactId>
        <packaging>jarpackaging>
        
        <name>basic-crudname>
        <url>https://github.com/zq2599url>
    
        
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-dependenciesartifactId>
    
                    <version>${springboot.version}version>
                    <type>pomtype>
                    <scope>importscope>
                dependency>
            dependencies>
        dependencyManagement>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
    
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-configuration-processorartifactId>
                <optional>trueoptional>
            dependency>
    
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
    
                
                <exclusions>
                    <exclusion>
                        <groupId>junitgroupId>
                        <artifactId>junitartifactId>
                    exclusion>
                exclusions>
    
            dependency>
    
            
            <dependency>
                <groupId>org.junit.jupitergroupId>
                <artifactId>junit-jupiter-apiartifactId>
                <scope>testscope>
            dependency>
    
            <dependency>
                <groupId>org.junit.jupitergroupId>
                <artifactId>junit-jupiter-engineartifactId>
                <scope>testscope>
            dependency>
    
            
            <dependency>
                <groupId>co.elastic.clientsgroupId>
                <artifactId>elasticsearch-javaartifactId>
            dependency>
    
            <dependency>
                <groupId>com.fasterxml.jackson.coregroupId>
                <artifactId>jackson-databindartifactId>
            dependency>
    
            
            <dependency>
                <groupId>jakarta.jsongroupId>
                <artifactId>jakarta.json-apiartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>
        dependencies>
    
        <build>
            <plugins>
                
                <plugin>
                    <groupId>org.apache.maven.pluginsgroupId>
                    <artifactId>maven-surefire-pluginartifactId>
                    <version>3.0.0-M4version>
                    <configuration>
                        <skipTests>falseskipTests>
                    configuration>
                plugin>
    
                <plugin>
                    <groupId>org.springframework.bootgroupId>
                    <artifactId>spring-boot-maven-pluginartifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombokgroupId>
                                <artifactId>lombokartifactId>
                            exclude>
                        excludes>
                    configuration>
                plugin>
            plugins>
    
            <resources>
                <resource>
                    <directory>src/main/resourcesdirectory>
                    <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> settingFn,
                    Function> 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> settingFn,
                           Function> 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> 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> 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) git@github.com:zq2599/blog_demos.git 该项目源码的仓库地址,ssh协议
    • 这个git项目中有多个文件夹,本次实战的源码在elasticsearch-tutorials文件夹下,如下图红框
      在这里插入图片描述
    • elasticsearch-tutorials是个父工程,里面有多个module,本篇实战的module是basic-crud,如下图红框
      在这里插入图片描述

    欢迎关注博客园:程序员欣宸

    学习路上,你不孤单,欣宸原创一路相伴...

  • 相关阅读:
    深度学习篇之tensorflow(3) ---架构介绍篇二
    vue从flask获取数据并显示
    word中设置页眉,首页不设置
    SQL Server如何获取GUID号
    什么是研发效能DevOps?研发效能方程式又是什么?
    “头疼”的俄罗斯开发者:不要再买我的软件了,收入不能提现
    【每日一句】名人金句学英语(1130)
    无线电HAM:业余无线电入门【无线电操作人员考证】【干货收藏】【网络安全进阶】
    Python之三大基本库——Numpy(1)
    【Spring Boot】035-Spring Boot 整合 MyBatis Plus
  • 原文地址:https://www.cnblogs.com/bolingcavalry/p/17658358.html