当前位置: 首页 > news >正文

SpringBoot集成Elasticsearch | Elasticsearch 8.x专属Java Client

SpringBoot集成Elasticsearch | Elasticsearch 8.x专属Java Client

  • 前言
    • 1. 版本说明与Maven依赖
    • 2. 配置文件(application.yml)
    • 3. 核心代码实现
      • 3.1 配置类(构建Java Client)
      • 3.2 工具类(封装8.x Java Client操作)
      • 3.3 实体类(Employee8xJavaClient)
      • 3.4 Controller层(测试接口)
    • 4. 测试步骤

SpringBoot集成Elasticsearch的三种核心方式,
Spring官方场景启动器、
Elasticsearch 7.x专属HLRC(High Level Rest Client)
Elasticsearch 8.x专属Java Client。

前言

Elasticsearch 8.x专属Java Client

Elasticsearch 8.x官方推出全新的Elasticsearch Java Client,替代了HLRC(HLRC在8.x中已弃用)。
它基于异步非阻塞IO,支持所有8.x新特性,但仅兼容8.x ES服务器,与7.x完全不兼容。

1. 版本说明与Maven依赖

ES 8.x Java Client需与ES服务器版本完全一致(如ES 8.14.0 → Java Client 8.14.0),且依赖结构与HLRC不同。

<dependencies><!-- Web依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- FastJSON --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><!-- 1. ES 8.x Java Client核心依赖(版本与ES服务器一致) --><dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>8.14.0</version></dependency><!-- 2. JSON处理器(Java Client依赖Jackson,需指定版本) --><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.2</version></dependency><!-- 3. 日志依赖(避免SLF4J绑定错误) --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.36</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-simple</artifactId><version>1.7.36</version></dependency>
</dependencies>

2. 配置文件(application.yml)

ES 8.x默认开启安全认证SSL加密,需配置账号密码、SSL信任策略(开发环境可关闭SSL验证,生产环境需配置证书)。

server:port: 8083 # 与前两种方式区分端口# ES 8.x Java Client配置
elasticsearch:java-client:cluster-name: es-cluster-8x # 集群名称(非必需)hosts: 127.0.0.1:9200 # 服务器地址(集群用逗号分隔)scheme: https # 8.x默认https(7.x默认http)# 安全认证(8.x默认开启,账号默认elastic,密码在ES首次启动时生成)username: elasticpassword: 你的ES 8.x密码# 超时配置(毫秒)connect-timeout: 5000socket-timeout: 30000# SSL配置(开发环境关闭证书验证,生产环境需配置cert-path)ssl:disable-verification: true # 关闭SSL证书验证(开发用,生产禁用)# cert-path: classpath:es-cert.pem # 生产环境:SSL证书路径

3. 核心代码实现

3.1 配置类(构建Java Client)

ES 8.x Java Client提供ElasticsearchClient(同步)和ElasticsearchAsyncClient(异步),此处以同步客户端为例。

package com.es.demo.config;import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;/*** ES 8.x Java Client配置类*/
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch.java-client")
public class Es8xJavaClientConfig {// 配置参数(与application.yml对应)private String clusterName;private String hosts;private String scheme;private String username;private String password;private int connectTimeout;private int socketTimeout;private SslConfig ssl;// 内部类:SSL配置@Datapublic static class SslConfig {private boolean disableVerification;private String certPath;}/*** 构建ElasticsearchClient(同步客户端,Spring单例注入)*/@Bean(name = "es8xElasticsearchClient")public ElasticsearchClient elasticsearchClient() throws Exception {// 1. 解析ES服务器地址HttpHost[] httpHosts = parseHttpHosts();// 2. 配置安全认证(8.x默认开启)CredentialsProvider credentialsProvider = new BasicCredentialsProvider();credentialsProvider.setCredentials(AuthScope.ANY,new UsernamePasswordCredentials(username, password));// 3. 配置SSL(开发环境关闭证书验证)RestClientBuilder builder = RestClient.builder(httpHosts).setHttpClientConfigCallback(httpClientBuilder -> {// 设置认证httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);// 配置SSLif (ssl.isDisableVerification()) {try {// 创建信任所有证书的SSL上下文(开发用,生产禁用)SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null, new TrustManager[]{new X509TrustManager() {@Overridepublic X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}@Overridepublic void checkClientTrusted(X509Certificate[] certs, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] certs, String authType) {}}}, new java.security.SecureRandom());httpClientBuilder.setSSLContext(sslContext);// 禁用主机名验证httpClientBuilder.setSSLHostnameVerifier((hostname, session) -> true);} catch (Exception e) {throw new RuntimeException("配置SSL失败", e);}}return httpClientBuilder;})// 配置超时时间.setRequestConfigCallback(requestConfigBuilder -> {requestConfigBuilder.setConnectTimeout(connectTimeout);requestConfigBuilder.setSocketTimeout(socketTimeout);return requestConfigBuilder;});// 4. 构建Transport(Java Client依赖RestClientTransport)ElasticsearchTransport transport = new RestClientTransport(builder.build(),new JacksonJsonpMapper() // JSON处理器(Jackson));// 5. 构建并返回同步客户端ElasticsearchClient client = new ElasticsearchClient(transport);log.info("ES 8.x Java Client初始化完成,集群名称:{},服务器地址:{}", clusterName, hosts);return client;}/*** 解析ES服务器地址为HttpHost数组*/private HttpHost[] parseHttpHosts() {String[] hostArray = hosts.split(",");HttpHost[] httpHosts = new HttpHost[hostArray.length];for (int i = 0; i < hostArray.length; i++) {String host = hostArray[i];String[] hostPort = host.split(":");if (hostPort.length != 2) {throw new RuntimeException("ES地址格式错误:" + host + "(正确格式:host:port)");}httpHosts[i] = new HttpHost(hostPort[0], Integer.parseInt(hostPort[1]), scheme);}return httpHosts;}
}

3.2 工具类(封装8.x Java Client操作)

8.x Java Client API与HLRC差异较大,需使用官方新API(如CreateIndexRequestSearchRequest的构建方式)。

package com.es.demo.util;import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.util.ObjectBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;/*** ES 8.x Java Client工具类*/
@Slf4j
@Component
@RequiredArgsConstructor
public class Es8xJavaClientUtil {// 注入8.x Java Client(与配置类中Bean名称一致)private final ElasticsearchClient es8xElasticsearchClient;// 常量定义public static final String KEYWORD_SUFFIX = ".keyword";public static final Time DEFAULT_TIMEOUT = Time.of(t -> t.time("3s"));/*** 1. 创建索引(带自定义mapping)*/public boolean createIndex(String indexName, String mapping) throws IOException {if (isIndexExist(indexName)) {log.warn("索引[{}]已存在,无需重复创建", indexName);return false;}// 构建创建索引请求CreateIndexRequest.Builder requestBuilder = CreateIndexRequest.of(b -> b.index(indexName));// 设置mapping(若传入)if (StringUtils.isNotBlank(mapping)) {requestBuilder.mappings(m -> m.withJson(mapping));}CreateIndexResponse response = es8xElasticsearchClient.indices().create(requestBuilder.build());log.info("索引[{}]创建成功,响应状态:{}", indexName, response.acknowledged());return response.acknowledged();}/*** 2. 判断索引是否存在*/public boolean isIndexExist(String indexName) throws IOException {ExistsRequest request = ExistsRequest.of(b -> b.index(indexName));boolean exists = es8xElasticsearchClient.indices().exists(request).value();log.debug("索引[{}]存在状态:{}", indexName, exists);return exists;}/*** 3. 删除索引*/public boolean deleteIndex(String indexName) throws IOException {if (!isIndexExist(indexName)) {log.warn("索引[{}]不存在,无需删除", indexName);return false;}DeleteIndexRequest request = DeleteIndexRequest.of(b -> b.index(indexName));es8xElasticsearchClient.indices().delete(request);log.info("索引[{}]删除成功", indexName);return true;}/*** 4. 添加文档(自动生成docId)*/public String addDoc(String indexName, Object data) throws IOException {return addDoc(indexName, UUID.randomUUID().toString().replace("-", "").toUpperCase(), data);}/*** 4. 重载:添加文档(自定义docId)*/public String addDoc(String indexName, String docId, Object data) throws IOException {// 构建索引请求IndexRequest<Object> request = IndexRequest.of(b -> b.index(indexName).id(docId).document(data).timeout(DEFAULT_TIMEOUT));IndexResponse response = es8xElasticsearchClient.index(request);log.info("文档添加成功:索引[{}],docId[{}],响应状态[{}]",indexName, response.id(), response.result().jsonValue());return response.id();}/*** 5. 根据docId删除文档*/public boolean deleteDocByDocId(String indexName, String docId) throws IOException {if (!isDocExist(indexName, docId)) {log.warn("文档不存在:索引[{}],docId[{}]", indexName, docId);return false;}DeleteRequest request = DeleteRequest.of(b -> b.index(indexName).id(docId).timeout(DEFAULT_TIMEOUT));DeleteResponse response = es8xElasticsearchClient.delete(request);log.info("文档删除成功:索引[{}],docId[{}],响应状态[{}]",indexName, docId, response.result().jsonValue());return true;}/*** 6. 根据docId更新文档(部分更新)*/public boolean updateDocByDocId(String indexName, String docId, Map<String, Object> updateData) throws IOException {if (!isDocExist(indexName, docId)) {log.warn("文档不存在:索引[{}],docId[{}],无法更新", indexName, docId);return false;}// 构建更新请求(传入Map类型的更新数据)UpdateRequest<Object, Object> request = UpdateRequest.of(b -> b.index(indexName).id(docId).doc(updateData).timeout(DEFAULT_TIMEOUT).refresh(r -> r.waitFor(true)) // 实时刷新);UpdateResponse<Object> response = es8xElasticsearchClient.update(request, Object.class);log.info("文档更新成功:索引[{}],docId[{}],响应状态[{}]",indexName, docId, response.result().jsonValue());return true;}/*** 7. 根据docId查询文档(返回Map)*/public Map<String, JsonData> getDocByDocId(String indexName, String docId) throws IOException {if (!isDocExist(indexName, docId)) {log.warn("文档不存在:索引[{}],docId[{}]", indexName, docId);return null;}GetRequest request = GetRequest.of(b -> b.index(indexName).id(docId));GetResponse<Object> response = es8xElasticsearchClient.get(request, Object.class);// 转换为Map<String, JsonData>(方便解析)return response.sourceAsMap();}/*** 8. 判断文档是否存在*/public boolean isDocExist(String indexName, String docId) throws IOException {ExistsRequest request = ExistsRequest.of(b -> b.index(indexName).id(docId));boolean exists = es8xElasticsearchClient.exists(request).value();log.debug("文档存在状态:索引[{}],docId[{}],存在[{}]", indexName, docId, exists);return exists;}/*** 9. 高亮查询(姓名模糊匹配,分页排序)*/public <T> List<T> searchHighlight(String indexName,String keyword,Integer pageNum,Integer pageSize,String sortField,Class<T> clazz) throws IOException {// 1. 构建查询条件(matchQuery)Function<MatchQuery.Builder, ObjectBuilder<MatchQuery>> matchQuery = q -> q.field("name").query(FieldValue.of(keyword)).analyzer("ik_smart");Query query = Query.of(b -> b.match(matchQuery));// 2. 构建高亮配置HighlightField highlightField = HighlightField.of(b -> b.preTags("<span style='color:red'>").postTags("</span>").requireFieldMatch(false));Highlight highlight = Highlight.of(b -> b.fields("name", highlightField));// 3. 构建搜索请求SearchRequest request = SearchRequest.of(b -> b.index(indexName).query(query).highlight(highlight).from((pageNum - 1) * pageSize).size(pageSize)// 配置排序.sort(s -> {if (StringUtils.isNotBlank(sortField)) {String sortFieldWithSuffix = sortField.contains(".") ? sortField : sortField + KEYWORD_SUFFIX;s.field(f -> f.field(sortFieldWithSuffix).order(SortOrder.Asc));}return s;}));// 4. 执行查询并解析结果SearchResponse<T> response = es8xElasticsearchClient.search(request, clazz);List<Hit<T>> hits = response.hits().hits();List<T> resultList = new ArrayList<>();for (Hit<T> hit : hits) {T doc = hit.source();// 处理高亮(此处需根据实体类字段手动设置,8.x Java Client需显式映射)if (hit.highlight() != null && hit.highlight().containsKey("name")) {// 假设实体类有setName方法,通过反射或FastJSON设置高亮值String highlightName = hit.highlight().get("name").get(0);if (doc instanceof Map) {((Map<String, Object>) doc).put("name", highlightName);} else {// 若为POJO,可通过FastJSON转换后设置String json = com.alibaba.fastjson.JSON.toJSONString(doc);Map<String, Object> map = com.alibaba.fastjson.JSON.parseObject(json);map.put("name", highlightName);doc = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson.JSON.toJSONString(map), clazz);}}resultList.add(doc);}log.info("高亮查询完成:索引[{}],匹配总数[{}],分页[{}页/{}条]",indexName, response.hits().total().value(), pageNum, pageSize);return resultList;}
}

3.3 实体类(Employee8xJavaClient)

与前两种方式一致,仅用于数据传输。

package com.es.demo.entity;import lombok.Data;import java.math.BigDecimal;
import java.util.Date;/*** 员工实体(用于ES 8.x Java Client)*/
@Data
public class Employee8xJavaClient {private String docId; // 文档IDprivate String jobNo; // 工号private String name; // 姓名private String job; // 岗位private Integer age; // 年龄private BigDecimal salary; // 薪资private Date jobDay; // 入职时间private String remark; // 备注
}

3.4 Controller层(测试接口)

调用8.x工具类实现测试接口,注意处理SSL和认证相关异常。

环境提示

  • 开发/测试环境:创建索引时可暂不传mapping,利用ES动态映射快速验证接口连通性;
  • 生产环境:必须传入自定义mapping(当前代码已配置),明确字段类型、分词器及格式规范,且需提前在集群中预创建索引并关闭动态映射,避免线上数据结构混乱。
package com.es.demo.controller;import com.es.demo.entity.Employee8xJavaClient;
import com.es.demo.util.Es8xJavaClientUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;@Slf4j
@RestController
@RequestMapping("/api/java-client8x/employee")
@RequiredArgsConstructor
public class Employee8xJavaClientController {private final Es8xJavaClientUtil es8xJavaClientUtil;private static final String EMPLOYEE_INDEX = "employee_java_client_8x";/*** 1. 创建员工索引(带mapping)*/@GetMapping("/index/create")public String createIndex() {String mapping = "{\n" +"  \"properties\": {\n" +"    \"jobNo\": {\"type\": \"keyword\"},\n" +"    \"name\": {\n" +"      \"type\": \"text\",\n" +"      \"analyzer\": \"ik_max_word\",\n" +"      \"search_analyzer\": \"ik_smart\"\n" +"    },\n" +"    \"job\": {\"type\": \"keyword\"},\n" +"    \"age\": {\"type\": \"integer\"},\n" +"    \"salary\": {\"type\": \"double\"},\n" +"    \"jobDay\": {\n" +"      \"type\": \"date\",\n" +"      \"format\": \"yyyy-MM-dd\"\n" +"    },\n" +"    \"remark\": {\n" +"      \"type\": \"text\",\n" +"      \"analyzer\": \"ik_smart\"\n" +"    }\n" +"  }\n" +"}";try {boolean result = es8xJavaClientUtil.createIndex(EMPLOYEE_INDEX, mapping);return result ? "索引创建成功" : "索引已存在";} catch (IOException e) {log.error("创建索引失败", e);return "索引创建失败:" + e.getMessage();}}/*** 2. 添加员工文档*/@PostMapping("/save")public String save(@RequestBody Employee8xJavaClient employee) {try {String docId = es8xJavaClientUtil.addDoc(EMPLOYEE_INDEX, employee);return "员工添加成功,docId:" + docId;} catch (IOException e) {log.error("添加员工失败", e);return "添加员工失败:" + e.getMessage();}}/*** 3. 高亮查询员工*/@GetMapping("/search-highlight")public List<Employee8xJavaClient> searchHighlight(@RequestParam String name,@RequestParam(defaultValue = "1") Integer pageNum,@RequestParam(defaultValue = "10") Integer pageSize) {try {// 调用工具类高亮查询(按jobNo排序)return es8xJavaClientUtil.searchHighlight(EMPLOYEE_INDEX,name,pageNum,pageSize,"jobNo", // 排序字段Employee8xJavaClient.class // 返回实体类类型);} catch (IOException e) {log.error("高亮查询员工失败(姓名关键词:{})", name, e);return null;}}/*** 4. 测试示例:添加单个员工*/@GetMapping("/test/save")public String testSave(@RequestParam String jobNo,@RequestParam String name,@RequestParam String job,@RequestParam Integer age,@RequestParam BigDecimal salary,@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date jobDay,@RequestParam(required = false) String remark) {Employee8xJavaClient employee = new Employee8xJavaClient();// 赋值请求参数到员工实体employee.setJobNo(jobNo);employee.setName(name);employee.setJob(job);employee.setAge(age);employee.setSalary(salary);employee.setJobDay(jobDay);employee.setRemark(remark);// docId留空,由工具类自动生成UUIDtry {String docId = es8xJavaClientUtil.addDoc(EMPLOYEE_INDEX, employee);return "测试添加员工成功,docId:" + docId;} catch (IOException e) {log.error("测试添加员工失败", e);return "测试添加员工失败:" + e.getMessage();}}
}

4. 测试步骤

  1. 启动ES 8.x服务器(确保9200端口可访问,且已记录初始化时生成的elastic账号密码,需与application.yml配置一致);
  2. 启动SpringBoot项目(若启动时出现SSL相关异常,检查application.yml中ssl.disable-verification: true配置是否生效);
  3. 调用创建索引接口:访问http://localhost:8083/api/java-client8x/employee/index/create,返回“索引创建成功”即可;
  4. 调用测试添加接口:访问http://localhost:8083/api/java-client8x/employee/test/save?jobNo=2024003&name=王五&job=后端开发&age=30&salary=28000&jobDay=2022-05-10&remark=架构师,获取返回的docId;
  5. 调用高亮查询接口:访问http://localhost:8083/api/java-client8x/employee/search-highlight?name=王&pageNum=1&pageSize=10,验证返回结果中name字段是否有红色高亮标签,且数据匹配正确。
http://www.dtcms.com/a/529430.html

相关文章:

  • 网站开发项目经理工资珠海市城乡规划建设局网站
  • 深圳网站优化软件论坛模板建站
  • Jenkins从节点配置报错处理:从搭建到任务调度,参数详解与实战指南
  • 物联网多类型设备列表的智能化设计与实现
  • 物联网运维中的自适应容灾备份与快速恢复机制设计
  • 商丘住房和城乡建设厅网站wordpress去掉顶部工具栏
  • 保定网站模板建站网站销售都怎么做的
  • 黄冈网站建设效果中国十大mro电商企业
  • 太原中企动力网站建设国外数码印花图案设计网站
  • 小红书开放平台获取笔记评论API接口指南(2025年最新版)
  • 如何制作网站导航栏中国百强城市榜单发布2021
  • Container
  • 京东网站建设目标wordpress前台登入注册
  • wlblang新式超高级现代编程语言 wlbai智能AI程序说明
  • Compose笔记(五十二)--FilledIconButton
  • 深南花园裙楼+网站建设创业项目网站建设规划
  • 无人机:你的随身摄影师已上线
  • 哪家上市公司做视频网站wordpress if include
  • IDEA的基本设置和使用
  • 【Linux】用户管理及优化
  • 算法题:安排邮筒
  • jdk动态代理实现
  • 响应式网站一般怎么设计网站主要盈利模式
  • 【C++】C++ 中的 map
  • 4. Qt 元对象系统 属性系统
  • 阿里云 有企业 网站吗做网站找云无限
  • 策划网站建设价格三水 网站建设
  • C++容器array
  • 智能仓储物流6大系统OMS、WES、WMS、WCS、AGV、数字孪生技术,到底都管什么?
  • 徐州专门做网站百度指数怎么用