SpringBoot2集成Elasticsearch8(使用spring-boot-starter-data-elasticsearch)

news2025/3/26 4:38:46

写在前面

使用spring-boot-starter-data-elasticsearch集成Elasticsearch8?
What? 官方写的不支持啊?让我们来看下官方给出的版本建议。
在这里插入图片描述

官方地址:
https://docs.spring.io/spring-data/elasticsearch/reference/elasticsearch/versions.html

实际需求

大部分稳定的生产系统仍然使用的是SpringBoot2.x,即使使用最新的SpringBoot2.7.x,
也会发现使用spring-boot-starter-data-elasticsearch去连接Elasticsearch8也是连不上的。具体报错就不贴了,但是你说我们升级SpringBoot3?当然可以,但是jdk也得升级,
生产系统那种运行的稳定的一批的客户能同意,领导能同意么?显然是,很难。

深入源码

废话不多说,先说改造方案,就是我们自己使用自定义的类覆盖官方的默认实现。
案例如下:

/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the Elastic License
 * 2.0 and the Server Side Public License, v 1; you may not use this file except
 * in compliance with, at your election, the Elastic License 2.0 or the Server
 * Side Public License, v 1.
 */
package org.elasticsearch.action;

import cn.hutool.core.util.StrUtil;
import org.elasticsearch.Version;
import org.elasticsearch.action.support.WriteRequest;
import org.elasticsearch.action.support.WriteRequest.RefreshPolicy;
import org.elasticsearch.action.support.WriteResponse;
import org.elasticsearch.action.support.replication.ReplicationResponse;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.xcontent.StatusToXContentObject;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.Index;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.seqno.SequenceNumbers;
import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.xcontent.XContentBuilder;
import org.elasticsearch.xcontent.XContentParser;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Locale;
import java.util.Objects;

import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken;
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_PRIMARY_TERM;
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_SEQ_NO;

/**
 * A base class for the response of a write operation that involves a single doc
 */
public abstract class DocWriteResponse extends ReplicationResponse implements WriteResponse, StatusToXContentObject {

    private static final String _SHARDS = "_shards";
    private static final String _INDEX = "_index";
    private static final String _TYPE = "_type";
    private static final String _ID = "_id";
    private static final String _VERSION = "_version";
    private static final String _SEQ_NO = "_seq_no";
    private static final String _PRIMARY_TERM = "_primary_term";
    private static final String RESULT = "result";
    private static final String FORCED_REFRESH = "forced_refresh";

    /**
     * An enum that represents the results of CRUD operations, primarily used to communicate the type of
     * operation that occurred.
     */
    public enum Result implements Writeable {
        CREATED(0),
        UPDATED(1),
        DELETED(2),
        NOT_FOUND(3),
        NOOP(4);

        private final byte op;
        private final String lowercase;

        Result(int op) {
            this.op = (byte) op;
            this.lowercase = this.name().toLowerCase(Locale.ROOT);
        }

        public byte getOp() {
            return op;
        }

        public String getLowercase() {
            return lowercase;
        }

        public static Result readFrom(StreamInput in) throws IOException {
            Byte opcode = in.readByte();
            switch (opcode) {
                case 0:
                    return CREATED;
                case 1:
                    return UPDATED;
                case 2:
                    return DELETED;
                case 3:
                    return NOT_FOUND;
                case 4:
                    return NOOP;
                default:
                    throw new IllegalArgumentException("Unknown result code: " + opcode);
            }
        }

        @Override
        public void writeTo(StreamOutput out) throws IOException {
            out.writeByte(op);
        }
    }

    private final ShardId shardId;
    private final String id;
    private final String type;
    private final long version;
    private final long seqNo;
    private final long primaryTerm;
    private boolean forcedRefresh;
    protected final Result result;

    public DocWriteResponse(ShardId shardId, String type, String id, long seqNo, long primaryTerm, long version, Result result) {
        this.shardId = Objects.requireNonNull(shardId);
        this.type = StrUtil.isEmpty(type)?"_doc":type;
        this.id = Objects.requireNonNull(id);
        this.seqNo = seqNo;
        this.primaryTerm = primaryTerm;
        this.version = version;
        this.result = Objects.requireNonNull(result);
    }

    // needed for deserialization
    protected DocWriteResponse(ShardId shardId, StreamInput in) throws IOException {
        this.shardId = shardId;
        String typeTmp = in.readString();
        type =  StrUtil.isEmpty(typeTmp)?"_doc":typeTmp;;
        id = in.readString();
        version = in.readZLong();
        seqNo = in.readZLong();
        primaryTerm = in.readVLong();
        forcedRefresh = in.readBoolean();
        result = Result.readFrom(in);
    }

    /**
     * Needed for deserialization of single item requests in {@link org.elasticsearch.action.index.IndexAction} and BwC
     * deserialization path
     */
    protected DocWriteResponse(StreamInput in) throws IOException {
        super(in);
        shardId = new ShardId(in);
        String typeTmp = in.readString();
        type =  StrUtil.isEmpty(typeTmp)?"_doc":typeTmp;;
        id = in.readString();
        version = in.readZLong();
        if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha1)) {
            seqNo = in.readZLong();
            primaryTerm = in.readVLong();
        } else {
            seqNo = UNASSIGNED_SEQ_NO;
            primaryTerm = UNASSIGNED_PRIMARY_TERM;
        }
        forcedRefresh = in.readBoolean();
        result = Result.readFrom(in);
    }

    /**
     * The change that occurred to the document.
     */
    public Result getResult() {
        return result;
    }

    /**
     * The index the document was changed in.
     */
    public String getIndex() {
        return this.shardId.getIndexName();
    }

    /**
     * The exact shard the document was changed in.
     */
    public ShardId getShardId() {
        return this.shardId;
    }

    /**
     * The type of the document changed.
     *
     * @deprecated Types are in the process of being removed.
     */
    @Deprecated
    public String getType() {
        return this.type;
    }

    /**
     * The id of the document changed.
     */
    public String getId() {
        return this.id;
    }

    /**
     * Returns the current version of the doc.
     */
    public long getVersion() {
        return this.version;
    }

    /**
     * Returns the sequence number assigned for this change. Returns {@link SequenceNumbers#UNASSIGNED_SEQ_NO} if the operation
     * wasn't performed (i.e., an update operation that resulted in a NOOP).
     */
    public long getSeqNo() {
        return seqNo;
    }

    /**
     * The primary term for this change.
     *
     * @return the primary term
     */
    public long getPrimaryTerm() {
        return primaryTerm;
    }

    /**
     * Did this request force a refresh? Requests that set {@link WriteRequest#setRefreshPolicy(RefreshPolicy)} to
     * {@link RefreshPolicy#IMMEDIATE} will always return true for this. Requests that set it to {@link RefreshPolicy#WAIT_UNTIL} will
     * only return true here if they run out of refresh listener slots (see {@link IndexSettings#MAX_REFRESH_LISTENERS_PER_SHARD}).
     */
    public boolean forcedRefresh() {
        return forcedRefresh;
    }

    @Override
    public void setForcedRefresh(boolean forcedRefresh) {
        this.forcedRefresh = forcedRefresh;
    }

    /** returns the rest status for this response (based on {@link ShardInfo#status()} */
    @Override
    public RestStatus status() {
        return getShardInfo().status();
    }

    /**
     * Return the relative URI for the location of the document suitable for use in the {@code Location} header. The use of relative URIs is
     * permitted as of HTTP/1.1 (cf. https://tools.ietf.org/html/rfc7231#section-7.1.2).
     *
     * @param routing custom routing or {@code null} if custom routing is not used
     * @return the relative URI for the location of the document
     */
    public String getLocation(@Nullable String routing) {
        final String encodedIndex;
        final String encodedType;
        final String encodedId;
        final String encodedRouting;
        try {
            // encode the path components separately otherwise the path separators will be encoded
            encodedIndex = URLEncoder.encode(getIndex(), "UTF-8");
            encodedType = URLEncoder.encode(getType(), "UTF-8");
            encodedId = URLEncoder.encode(getId(), "UTF-8");
            encodedRouting = routing == null ? null : URLEncoder.encode(routing, "UTF-8");
        } catch (final UnsupportedEncodingException e) {
            throw new AssertionError(e);
        }
        final String routingStart = "?routing=";
        final int bufferSizeExcludingRouting = 3 + encodedIndex.length() + encodedType.length() + encodedId.length();
        final int bufferSize;
        if (encodedRouting == null) {
            bufferSize = bufferSizeExcludingRouting;
        } else {
            bufferSize = bufferSizeExcludingRouting + routingStart.length() + encodedRouting.length();
        }
        final StringBuilder location = new StringBuilder(bufferSize);
        location.append('/').append(encodedIndex);
        location.append('/').append(encodedType);
        location.append('/').append(encodedId);
        if (encodedRouting != null) {
            location.append(routingStart).append(encodedRouting);
        }

        return location.toString();
    }

    public void writeThin(StreamOutput out) throws IOException {
        super.writeTo(out);
        writeWithoutShardId(out);
    }

    @Override
    public void writeTo(StreamOutput out) throws IOException {
        super.writeTo(out);
        shardId.writeTo(out);
        writeWithoutShardId(out);
    }

    private void writeWithoutShardId(StreamOutput out) throws IOException {
        out.writeString(type);
        out.writeString(id);
        out.writeZLong(version);
        if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha1)) {
            out.writeZLong(seqNo);
            out.writeVLong(primaryTerm);
        }
        out.writeBoolean(forcedRefresh);
        result.writeTo(out);
    }

    @Override
    public final XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
        builder.startObject();
        innerToXContent(builder, params);
        builder.endObject();
        return builder;
    }

    public XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
        ReplicationResponse.ShardInfo shardInfo = getShardInfo();
        builder.field(_INDEX, shardId.getIndexName());
        builder.field(_TYPE, type);
        builder.field(_ID, id).field(_VERSION, version).field(RESULT, getResult().getLowercase());
        if (forcedRefresh) {
            builder.field(FORCED_REFRESH, true);
        }
        builder.field(_SHARDS, shardInfo);
        if (getSeqNo() >= 0) {
            builder.field(_SEQ_NO, getSeqNo());
            builder.field(_PRIMARY_TERM, getPrimaryTerm());
        }
        return builder;
    }

    /**
     * Parse the output of the {@link #innerToXContent(XContentBuilder, Params)} method.
     *
     * This method is intended to be called by subclasses and must be called multiple times to parse all the information concerning
     * {@link DocWriteResponse} objects. It always parses the current token, updates the given parsing context accordingly
     * if needed and then immediately returns.
     */
    protected static void parseInnerToXContent(XContentParser parser, Builder context) throws IOException {
        XContentParser.Token token = parser.currentToken();
        ensureExpectedToken(XContentParser.Token.FIELD_NAME, token, parser);

        String currentFieldName = parser.currentName();
        token = parser.nextToken();

        if (token.isValue()) {
            if (_INDEX.equals(currentFieldName)) {
                // index uuid and shard id are unknown and can't be parsed back for now.
                context.setShardId(new ShardId(new Index(parser.text(), IndexMetadata.INDEX_UUID_NA_VALUE), -1));
            } else if (_TYPE.equals(currentFieldName)) {
                context.setType(parser.text());
            } else if (_ID.equals(currentFieldName)) {
                context.setId(parser.text());
            } else if (_VERSION.equals(currentFieldName)) {
                context.setVersion(parser.longValue());
            } else if (RESULT.equals(currentFieldName)) {
                String result = parser.text();
                for (Result r : Result.values()) {
                    if (r.getLowercase().equals(result)) {
                        context.setResult(r);
                        break;
                    }
                }
            } else if (FORCED_REFRESH.equals(currentFieldName)) {
                context.setForcedRefresh(parser.booleanValue());
            } else if (_SEQ_NO.equals(currentFieldName)) {
                context.setSeqNo(parser.longValue());
            } else if (_PRIMARY_TERM.equals(currentFieldName)) {
                context.setPrimaryTerm(parser.longValue());
            }
        } else if (token == XContentParser.Token.START_OBJECT) {
            if (_SHARDS.equals(currentFieldName)) {
                context.setShardInfo(ShardInfo.fromXContent(parser));
            } else {
                parser.skipChildren(); // skip potential inner objects for forward compatibility
            }
        } else if (token == XContentParser.Token.START_ARRAY) {
            parser.skipChildren(); // skip potential inner arrays for forward compatibility
        }
    }

    /**
     * Base class of all {@link DocWriteResponse} builders. These {@link DocWriteResponse.Builder} are used during
     * xcontent parsing to temporarily store the parsed values, then the {@link Builder#build()} method is called to
     * instantiate the appropriate {@link DocWriteResponse} with the parsed values.
     */
    public abstract static class Builder {

        protected ShardId shardId = null;
        protected String type = null;
        protected String id = null;
        protected Long version = null;
        protected Result result = null;
        protected boolean forcedRefresh;
        protected ShardInfo shardInfo = null;
        protected long seqNo = UNASSIGNED_SEQ_NO;
        protected long primaryTerm = UNASSIGNED_PRIMARY_TERM;

        public ShardId getShardId() {
            return shardId;
        }

        public void setShardId(ShardId shardId) {
            this.shardId = shardId;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            type =  StrUtil.isEmpty(type)?"_doc":type;;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public void setVersion(Long version) {
            this.version = version;
        }

        public void setResult(Result result) {
            this.result = result;
        }

        public void setForcedRefresh(boolean forcedRefresh) {
            this.forcedRefresh = forcedRefresh;
        }

        public void setShardInfo(ShardInfo shardInfo) {
            this.shardInfo = shardInfo;
        }

        public void setSeqNo(long seqNo) {
            this.seqNo = seqNo;
        }

        public void setPrimaryTerm(long primaryTerm) {
            this.primaryTerm = primaryTerm;
        }

        public abstract DocWriteResponse build();
    }
}

写在最后

如果想要了解改的内容,可以找到官方源码对比下实现,然后自己DEBUG一下,
然后就可以知道Elasticsearch7和Elasticsearch8在连接时候的区别了。
如果要问为什么,答案就是可以自己动态创建索引,还可以指定字段类型,就是
这么香而已。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2321718.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【平台优化】持续调度参数在高负载大集群中的影响

持续调度参数在高负载大集群中的影响 背景介绍2种调度通信方式对集群的影响社区相关的讨论结论 背景介绍 这几年经历了我们大数据的Yarn集群的几次扩容,集群从原先的800多台增加到1300多台到现在的1600多台,在集群规模不断增加的过程中,有遇…

ElasticSearch 可观测性最佳实践

ElasticSearch 概述 ElasticSearch 是一个开源的高扩展的分布式全文检索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别(大数据时代)的数据。ES 也使用 Java 开…

(一)飞行器的姿态欧拉角, 欧拉旋转, 完全数学推导(基于坐标基的变换矩阵).(偏航角,俯仰角,横滚角)

(这篇写的全是基矢变换矩阵)不是坐标变换矩阵,坐标变换矩阵的话转置一下,之后会有推导. 是通过M转置变换到P撇点.

基于Spring Boot + Vue的银行管理系统设计与实现

基于Spring Boot Vue的银行管理系统设计与实现 一、引言 随着金融数字化进程加速,传统银行业务向线上化转型成为必然趋势。本文设计并实现了一套基于Spring Boot Vue的银行管理系统,通过模块化架构满足用户、银行职员、管理员三类角色的核心业务需求…

数据库基础知识点(系列一)

1.数据库的发展历史分哪几个阶段?各有什么特点? 答:数据库技术经历了人工管理阶段、文件系统阶段和数据库系统三个阶段。 1)人工管理阶段 这个时期数据管理的特点是: 数据由计算或处理它的程序自行携带…

JVM常用概念之身份哈希码

问题 当我们调用Object.hashCode时,如果没有用户没有提供哈希码,会发生什么? System.identityHashCode如何工作?它是否获取对象地址? 基础知识 在 Java 中,每个对象都有equals和hashCode ,即…

vue 对接 paypal 订阅和支付

一个是支付一个是订阅,写的时候尝试把他们放到一个里面,但是会报错,所以分开写了 我们的页面,前三个为订阅最后一个是支付,我把他们放到一个数组里面循环展示的,所以我们判断的时候只要判断id是否为4&#…

基于javaweb的SpringBoot实习管理系统设计与实现(源码+文档+部署讲解)

技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

流影---开源网络流量分析平台(一)(小白超详细)

目录 流影介绍 一、技术架构与核心技术 二、核心功能与特性 流影部署 流影介绍 一、技术架构与核心技术 模块化引擎设计 流影采用四层模块化架构:流量探针(数据采集)、网络行为分析引擎(特征提取)、威胁检测引擎&…

Oracle 数据库安全评估(DBSAT)简明过程

下载DBSAT 从这里下载。 实际是从MOS中下载,即:Oracle Database Security Assessment Tool (DBSAT) (Doc ID 2138254.1)。 最新版本为3.1.0 (July 2024),名为dbsat.zip,近45MB。 $ ls -lh dbsat.zip -rw-rw-r-- 1 oracle oins…

【T2I】Divide Bind Your Attention for Improved Generative Semantic Nursing

CODE: GitHub - boschresearch/Divide-and-Bind: Official implementation of "Divide & Bind Your Attention for Improved Generative Semantic Nursing" (BMVC 2023 Oral) ABSTRACT 新兴的大规模文本到图像生成模型,如稳定扩散(SD),已…

【2025】基于springboot+uniapp的企业培训打卡小程序设计与实现(源码、万字文档、图文修改、调试答疑)

基于 Spring Boot uniapp 的企业培训打卡小程序设计与实现 系统功能结构图如下: 一、课题背景 在当今快节奏的商业环境中,企业培训对于员工的成长和企业的发展至关重要。为了满足企业对高效培训管理和员工便捷学习的需求,基于 Spring Boot …

探索AI的无限可能,体验智能对话的未来,大模型 API 演示

探索AI的无限可能,体验智能对话的未来,大模型 API 演示 效果展示: 项目概述 这是一个基于 Vue 3 TypeScript Vite 构建的 Vista AI 演示项目,旨在提供一个简洁易用的界面来展示 Vista AI 大语言模型的能力。项目包含 API 演示…

26考研——图_图的存储(6)

408答疑 文章目录 二、图的存储图的存储相关概念邻接矩阵存储方式邻接矩阵的定义顶点的度计算邻接矩阵的特点邻接矩阵的局限性 应用场景邻接矩阵的幂次意义(了解即可) 邻接表存储方式邻接表定义邻接表结构邻接表的特点 邻接矩阵和邻接表的适用性差异十字…

datawhale组队学习--大语言模型—task4:Transformer架构及详细配置

第五章 模型架构 在前述章节中已经对预训练数据的准备流程(第 4 章)进行了介绍。本章主 要讨论大语言模型的模型架构选择,主要围绕 Transformer 模型(第 5.1 节)、详细 配置(第 5.2 节)、主流架…

《TCP/IP网络编程》学习笔记 | Chapter 21:异步通知 I/O 模型

《TCP/IP网络编程》学习笔记 | Chapter 21:异步通知 I/O 模型 《TCP/IP网络编程》学习笔记 | Chapter 21:异步通知 I/O 模型同步与异步同步异步对比同步 I/O 的缺点异步 I/O 的优点 理解异步通知 I/O 模型实现异步通知 I/O 模型WSAEventSelect 函数和通知…

Qt6相对Qt5的主要提升(AI总结)

我: Qt 6 相对于5 有哪些新功能? Qt 6 相对于 Qt 5 有诸多新功能和改进,以下是主要的新增特性: 1. 架构和核心库的重构 模块化设计:Qt 6 采用了更加灵活的模块化设计,开发者可以按需引入必要的功能模块&a…

MyBatis操作数据库进阶——动态SQL

动态 SQL 是根据程序运行时的条件灵活生成不同 SQL 语句‌的技术。它的核心目的是在不修改代码‌ 的前提下&#xff0c;通过条件判断、循环等逻辑&#xff0c;动态拼接 SQL 片段&#xff0c;解决传统 SQL 语句死板、难以应对复杂业务场景的问题。 一、<if> 标签 先来观…

使用LLama-Factory的简易教程(Llama3微调案例+详细步骤)

引言&#xff1a;一套快速实现 Llama3 中文微调的教程 主要参考&#xff1a;胖虎遛二狗的 B 站教学视频《【大模型微调】使用Llama Factory实现中文llama3微调》 ✅ 笔者简介&#xff1a;Wang Linyong&#xff0c;西工大&#xff0c;2023级&#xff0c;计算机技术 研究方向&am…

LabVIEW发电平台数据采集系统

本文详细介绍了基于LabVIEW的摇臂式波浪发电平台数据采集系统的设计与实现。通过整合LabVIEW软件与多种传感器技术&#xff0c;本系统能够有效提升数据采集的准确性和效率&#xff0c;为波浪能的利用和发电设备的优化提供科学依据。 ​ 项目背景 随着全球能源需求增长和环境保…