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

SpringBoot教程(二十八) | SpringBoot集成Elasticsearch(Java High Level Rest Client方式)

SpringBoot教程(二十八) | SpringBoot集成Elasticsearch(Java High Level Rest Client方式)

  • 前言
  • 添加maven依赖
  • yml配置
  • ElasticsearchConfig 连接配置类
  • EsUtil 工具类
  • 开始测试

前言

  • 由ES官方提供,代码语法和DSL语法相似(即Json格式的ES操作语法);
  • 用法灵活,可以自由使用;
  • SpringBoot 和 ES 版本的关联性较小;

比较推荐使用这种方式

添加maven依赖

我看网上一般都是用以下这个

<dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.15.2</version></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>7.15.2</version></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.15.2</version></dependency>

依赖解释

  • elasticsearch
    Elasticsearch的核心库,只是在客户端应用中与Elasticsearch集群交互,通常不需要直接包含这个依赖

  • elasticsearch-rest-client
    低级别的REST客户端,提供了基础的HTTP请求构建和执行能力,但不包括高级抽象,如索引、搜索等操作的封装

  • elasticsearch-rest-high-level-client
    高级REST客户端,它建立在elasticsearch-rest-client之上,提供了更高级别的抽象,如索引、搜索、更新等操作的封装。这使得与Elasticsearch的交互变得更加简单和直接。

yml配置

# es集群名称
elasticsearch.clusterName=single-node-cluster
#es用户名
elasticsearch.userName=elastic
#es密码
elasticsearch.password=elastic
# es host ip 地址(集群):本次使用的是单机模式
elasticsearch.hosts=127.0.0.1:9200
# es 请求方式
elasticsearch.scheme=http
# es 连接超时时间
elasticsearch.connectTimeOut=1000
# es socket 连接超时时间
elasticsearch.socketTimeOut=30000
# es 请求超时时间
elasticsearch.connectionRequestTimeOut=500
# es 最大连接数
elasticsearch.maxConnectNum=100
# es 每个路由的最大连接数
elasticsearch.maxConnectNumPerRoute=100

ElasticsearchConfig 连接配置类

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.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.util.ArrayList;
import java.util.List;/*** restHighLevelClient 客户端配置类*/
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticsearchConfig {// es host ip 地址(集群)private String hosts;// es 用户名private String userName;// es 密码private String password;// es 请求方式private String scheme;// es 集群名称private String clusterName;// es 连接超时时间private int connectTimeOut;// es socket 连接超时时间private int socketTimeOut;// es 请求超时时间private int connectionRequestTimeOut;// es 最大连接数private int maxConnectNum;// es 每个路由的最大连接数private int maxConnectNumPerRoute;/*** 如果@Bean没有指定bean的名称,那么这个bean的名称就是方法名*/@Bean(name = "restHighLevelClient")public RestHighLevelClient restHighLevelClient() {RestHighLevelClient restHighLevelClient = null;try {// 集群,拆分地址List<HttpHost> hostLists = new ArrayList<>();String[] hostList = hosts.split(",");for (String addr : hostList) {String host = addr.split(":")[0];String port = addr.split(":")[1];hostLists.add(new HttpHost(host, Integer.parseInt(port), scheme));}// 转换成 HttpHost 数组HttpHost[] httpHost = hostLists.toArray(new HttpHost[]{});// 构建连接对象RestClientBuilder builder = RestClient.builder(httpHost);// 设置用户名、密码CredentialsProvider credentialsProvider = new BasicCredentialsProvider();credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));// 连接延时配置builder.setRequestConfigCallback(requestConfigBuilder -> {requestConfigBuilder.setConnectTimeout(connectTimeOut);requestConfigBuilder.setSocketTimeout(socketTimeOut);requestConfigBuilder.setConnectionRequestTimeout(connectionRequestTimeOut);return requestConfigBuilder;});// 连接数配置builder.setHttpClientConfigCallback(httpClientBuilder -> {httpClientBuilder.setMaxConnTotal(maxConnectNum);httpClientBuilder.setMaxConnPerRoute(maxConnectNumPerRoute);httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);return httpClientBuilder;});restHighLevelClient = new RestHighLevelClient(builder);} catch (NumberFormatException e) {log.error("ES 连接池初始化异常");}return restHighLevelClient;}
}

EsUtil 工具类

工具类中,讲到的 index 其实就是 表名称

package com.example.springbootfull.elasticsearchclient.util;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.core.TimeValue;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
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;/*** es 的工具类** @author*/
@Slf4j
@Component
public class EsUtil {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** 关键字*/public static final String KEYWORD = ".keyword";/*** 创建索引** @param index 索引* @return*/public boolean createIndex(String index) throws IOException {if (isIndexExist(index)) {log.error("Index is exits!");return false;}//1.创建索引请求CreateIndexRequest request = new CreateIndexRequest(index);//2.执行客户端请求CreateIndexResponse response = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);log.info("创建索引{}成功", index);return response.isAcknowledged();}/*** 删除索引** @param index* @return*/public boolean deleteIndex(String index) throws IOException {if (!isIndexExist(index)) {log.error("Index is not exits!");return false;}//删除索引请求DeleteIndexRequest request = new DeleteIndexRequest(index);//执行客户端请求AcknowledgedResponse delete = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);log.info("删除索引{}成功", index);return delete.isAcknowledged();}/*** 判断索引是否存在** @param index* @return*/public boolean isIndexExist(String index) throws IOException {GetIndexRequest request = new GetIndexRequest(index);boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);return exists;}/*** 数据添加,正定ID** @param jsonObject 要增加的数据* @param index      索引,类似数据库* @param id         数据ID, 为null时es随机生成* @return*/public String addData(JSONObject jsonObject, String index, String id) throws IOException {//创建请求IndexRequest request = new IndexRequest(index);//规则 put /test_index/_doc/1request.id(id);request.timeout(TimeValue.timeValueSeconds(1));//将数据放入请求 jsonIndexRequest source = request.source(jsonObject, XContentType.JSON);//客户端发送请求IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);log.info("添加数据成功 索引为: {}, response 状态: {}, id为: {}", index, response.status().getStatus(), response.getId());return response.getId();}/*** 数据添加 随机id** @param jsonObject 要增加的数据* @param index      索引,类似数据库* @return*/public String addData(JSONObject jsonObject, String index) throws IOException {return addData(jsonObject, index, UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());}/*** 通过ID删除数据** @param index 索引,类似数据库* @param id    数据ID*/public void deleteDataById(String index, String id) throws IOException {//删除请求DeleteRequest request = new DeleteRequest(index, id);//执行客户端请求DeleteResponse delete = restHighLevelClient.delete(request, RequestOptions.DEFAULT);log.info("索引为: {}, id为: {}删除数据成功", index, id);}/*** 通过ID 更新数据** @param object 要增加的数据* @param index  索引,类似数据库* @param id     数据ID* @return*/public void updateDataById(Object object, String index, String id) throws IOException {//更新请求UpdateRequest update = new UpdateRequest(index, id);//保证数据实时更新//update.setRefreshPolicy("wait_for");update.timeout("1s");update.doc(JSON.toJSONString(object), XContentType.JSON);//执行更新请求UpdateResponse update1 = restHighLevelClient.update(update, RequestOptions.DEFAULT);log.info("索引为: {}, id为: {}, 更新数据成功", index, id);}/*** 通过ID 更新数据,保证实时性** @param object 要增加的数据* @param index  索引,类似数据库* @param id     数据ID* @return*/public void updateDataByIdNoRealTime(Object object, String index, String id) throws IOException {//更新请求UpdateRequest update = new UpdateRequest(index, id);//保证数据实时更新update.setRefreshPolicy("wait_for");update.timeout("1s");update.doc(JSON.toJSONString(object), XContentType.JSON);//执行更新请求UpdateResponse update1 = restHighLevelClient.update(update, RequestOptions.DEFAULT);log.info("索引为: {}, id为: {}, 更新数据成功", index, id);}/*** 通过ID获取数据** @param index  索引,类似数据库* @param id     数据ID* @param fields 需要显示的字段,逗号分隔(缺省为全部字段)* @return*/public Map<String, Object> searchDataById(String index, String id, String fields) throws IOException {GetRequest request = new GetRequest(index, id);if (StringUtils.isNotEmpty(fields)) {//只查询特定字段。如果需要查询所有字段则不设置该项。request.fetchSourceContext(new FetchSourceContext(true, fields.split(","), Strings.EMPTY_ARRAY));}GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);Map<String, Object> map = response.getSource();//为返回的数据添加idmap.put("id", response.getId());return map;}/*** 通过ID判断文档是否存在** @param index 索引,类似数据库* @param id    数据ID* @return*/public boolean existsById(String index, String id) throws IOException {GetRequest request = new GetRequest(index, id);//不获取返回的_source的上下文request.fetchSourceContext(new FetchSourceContext(false));request.storedFields("_none_");return restHighLevelClient.exists(request, RequestOptions.DEFAULT);}/*** 获取低水平客户端** @return*/public RestClient getLowLevelClient() {return restHighLevelClient.getLowLevelClient();}/*** 高亮结果集 特殊处理* map转对象 JSONObject.parseObject(JSONObject.toJSONString(map), Content.class)** @param searchResponse* @param highlightField*/public List<Map<String, Object>> setSearchResponse(SearchResponse searchResponse, String highlightField) {//解析结果ArrayList<Map<String, Object>> list = new ArrayList<>();for (SearchHit hit : searchResponse.getHits().getHits()) {Map<String, HighlightField> high = hit.getHighlightFields();HighlightField title = high.get(highlightField);hit.getSourceAsMap().put("id", hit.getId());Map<String, Object> sourceAsMap = hit.getSourceAsMap();//原来的结果//解析高亮字段,将原来的字段换为高亮字段if (title != null) {Text[] texts = title.fragments();String nTitle = "";for (Text text : texts) {nTitle += text;}//替换sourceAsMap.put(highlightField, nTitle);}list.add(sourceAsMap);}return list;}/*** 查询并分页** @param index          索引名称* @param query          查询条件* @param size           文档大小限制* @param from           从第几页开始* @param fields         需要显示的字段,逗号分隔(缺省为全部字段)* @param sortField      排序字段* @param highlightField 高亮字段* @return*/public List<Map<String, Object>> searchListData(String index,SearchSourceBuilder query,Integer size,Integer from,String fields,String sortField,String highlightField) throws IOException {SearchRequest request = new SearchRequest(index);SearchSourceBuilder builder = query;if (StringUtils.isNotEmpty(fields)) {//只查询特定字段。如果需要查询所有字段则不设置该项。builder.fetchSource(new FetchSourceContext(true, fields.split(","), Strings.EMPTY_ARRAY));}from = from <= 0 ? 0 : from * size;//设置确定结果要从哪个索引开始搜索的from选项,默认为0builder.from(from);builder.size(size);if (StringUtils.isNotEmpty(sortField)) {//排序字段,注意如果proposal_no是text类型会默认带有keyword性质,需要拼接.keywordbuilder.sort(sortField + ".keyword", SortOrder.ASC);}//高亮HighlightBuilder highlight = new HighlightBuilder();highlight.field(highlightField);//关闭多个高亮highlight.requireFieldMatch(false);highlight.preTags("<span style='color:red'>");highlight.postTags("</span>");builder.highlighter(highlight);//不返回源数据。只有条数之类的数据。//builder.fetchSource(false);request.source(builder);SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);log.error("==" + response.getHits().getTotalHits());if (response.status().getStatus() == 200) {// 解析对象return setSearchResponse(response, highlightField);}return null;}
}

开始测试

实体类

package com.example.springbootfull.elasticsearchclient.bean;import java.math.BigDecimal;
import java.util.Date;public class EmployeeInfo2 {private Long id;/*** 工号*/private String jobNo;/*** 姓名*/private String name;/*** 英文名*/private String englishName;/*** 工作岗位*/private String job;/*** 性别*/private Integer sex;/*** 年龄*/private Integer age;/*** 薪资*/private BigDecimal salary;/*** 入职时间*/private Date jobDay;/*** 备注*/private String remark;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getJobNo() {return jobNo;}public void setJobNo(String jobNo) {this.jobNo = jobNo;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getEnglishName() {return englishName;}public void setEnglishName(String englishName) {this.englishName = englishName;}public String getJob() {return job;}public void setJob(String job) {this.job = job;}public Integer getSex() {return sex;}public void setSex(Integer sex) {this.sex = sex;}public Integer getAge() {return age;}public void setAge(Integer age) {this.age = age;}public BigDecimal getSalary() {return salary;}public void setSalary(BigDecimal salary) {this.salary = salary;}public Date getJobDay() {return jobDay;}public void setJobDay(Date jobDay) {this.jobDay = jobDay;}public String getRemark() {return remark;}public void setRemark(String remark) {this.remark = remark;}public EmployeeInfo2() {}public EmployeeInfo2(Long id, String jobNo, String name, String englishName, String job, Integer sex, Integer age, BigDecimal salary, Date jobDay, String remark) {this.id = id;this.jobNo = jobNo;this.name = name;this.englishName = englishName;this.job = job;this.sex = sex;this.age = age;this.salary = salary;this.jobDay = jobDay;this.remark = remark;}@Overridepublic String toString() {return "EmployeeInfo{" +"id=" + id +", jobNo='" + jobNo + '\'' +", name='" + name + '\'' +", englishName='" + englishName + '\'' +", job='" + job + '\'' +", sex=" + sex +", age=" + age +", salary=" + salary +", jobDay=" + jobDay +", remark='" + remark + '\'' +'}';}
}

Controller类

package com.example.springbootfull.elasticsearchclient.controller;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.PropertyNamingStrategy;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.example.springbootfull.elasticsearchclient.bean.EmployeeInfo2;
import com.example.springbootfull.elasticsearchclient.util.EsUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.math.BigDecimal;
import java.text.SimpleDateFormat;@RestController
@RequestMapping("/employeeInfo")
public class EmployeeElasticController {@Autowiredprivate EsUtil esUtil;//==========esUtil================@RequestMapping("/createIndex")public String createIndex() throws Exception {esUtil.createIndex("employee_info_2");return "success";}@RequestMapping("/deleteIndex")public String deleteIndex() throws Exception {esUtil.deleteIndex("employee_info_2");return "success";}@RequestMapping("/save")public String save() throws Exception {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");EmployeeInfo2 employeeInfo = new EmployeeInfo2(6001L, "2001", "我去", "zhangsan", "Java", 1, 19, new BigDecimal("12500.01"), simpleDateFormat.parse("2019-09-10"), "备注");//可以帮我把驼峰命名的属性转成下划线格式的(如果你需要的话)SerializeConfig config = new SerializeConfig();config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;String json1 = JSON.toJSONString(employeeInfo, config);esUtil.addData(JSONObject.parseObject(json1), "employee_info_2", null);//不然直接//esUtil.addData((JSONObject) JSONObject.toJSON(employeeInfo), "employee_info_2", null);return "success";}}

执行save 方法后,可以看到 es 可视化页面上面 出现了 一条数据
(其中的id,由于我给了null,es就随机帮我生成了)
在这里插入图片描述
参考文章:
【1】SpringBoot整合ElasticSearch的两种方式
【2】elasticsearch7.9 java工具类restHighLevelClient精华整理JavaRESTClient
【3】三种方式实现Java对象转json下划线格式

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • Prometheus+Grafana普罗米修斯,搭建和使用
  • [最优化方法] 《最优化方法》个人问答式学习笔记 with LLM
  • Java语言程序设计基础篇_编程练习题*18.9 (以逆序输出一个字符串中的字符)
  • Resnet图像识别入门——激活函数
  • AI驱动的Web3革命:如何通过智能技术实现去中心化
  • prometheus删除指定metrics下收集的值
  • 【GIS开发教程分享】基于Mapbox技术栈,共享电动车项目实战
  • 内联汇编 (28)
  • 【云原生】docker 部署 Doris 数据库使用详解
  • 电力系统中的A类在线监测装置—APView400
  • 极狐GiLab 17.3 重点功能解读 升级指南
  • 2、PF-Net点云补全
  • 如何在ST官网下载HALL包和cube开发工具
  • STM32G474之TIM1输出PWM信号支持互补输出,死区时间和刹车
  • 【踩坑】Vue3项目正常跑动后页面空白问题
  • 【附node操作实例】redis简明入门系列—字符串类型
  • EventListener原理
  • vue2.0开发聊天程序(四) 完整体验一次Vue开发(下)
  • vue从创建到完整的饿了么(18)购物车详细信息的展示与删除
  • vue总结
  • 更好理解的面向对象的Javascript 1 —— 动态类型和多态
  • 每天10道Java面试题,跟我走,offer有!
  • 微信支付JSAPI,实测!终极方案
  • 我是如何设计 Upload 上传组件的
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • MPAndroidChart 教程:Y轴 YAxis
  • 扩展资源服务器解决oauth2 性能瓶颈
  • 新年再起“裁员潮”,“钢铁侠”马斯克要一举裁掉SpaceX 600余名员工 ...
  • ​​​【收录 Hello 算法】10.4 哈希优化策略
  • ​浅谈 Linux 中的 core dump 分析方法
  • ​软考-高级-信息系统项目管理师教程 第四版【第19章-配置与变更管理-思维导图】​
  • ​虚拟化系列介绍(十)
  • # 深度解析 Socket 与 WebSocket:原理、区别与应用
  • # 移动硬盘误操作制作为启动盘数据恢复问题
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • $.each()与$(selector).each()
  • (24)(24.1) FPV和仿真的机载OSD(三)
  • (day6) 319. 灯泡开关
  • (Oracle)SQL优化基础(三):看懂执行计划顺序
  • (附源码)计算机毕业设计SSM智慧停车系统
  • (接口封装)
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (五)Python 垃圾回收机制
  • (一)utf8mb4_general_ci 和 utf8mb4_unicode_ci 适用排序和比较规则场景
  • (转) Face-Resources
  • ***原理与防范
  • .NET CF命令行调试器MDbg入门(三) 进程控制
  • .NET Core 2.1路线图
  • .net core 外观者设计模式 实现,多种支付选择
  • .NET Framework 和 .NET Core 在默认情况下垃圾回收(GC)机制的不同(局部变量部分)
  • .NET Micro Framework初体验
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET/C# 在代码中测量代码执行耗时的建议(比较系统性能计数器和系统时间)
  • .NET:自动将请求参数绑定到ASPX、ASHX和MVC(菜鸟必看)
  • .net图片验证码生成、点击刷新及验证输入是否正确