springboot(spring-boot-starter-data-elasticsearch)操作es
环境:
springboot:2.3
es:6.8.5
jdk:21
需要提前创建好index
首先创建一个空的index(没有任何字段信息)
curl -u elastic:elastic -X PUT "192.168.1.192:19200/product?pretty" -H 'Content-Type: application/json' -d'
{}
'
再定义字段信息
curl -u elastic:elastic -H 'Content-Type: application/json' -XPOST "http://192.168.1.192:19200/product/product_type/_mapping?pretty" -d '
{
"properties" : {
"category" : {
"type" : "keyword"
},
"description" : {
"type" : "text"
},
"id" : {
"type" : "keyword"
},
"price" : {
"type" : "double"
},
"productName" : {
"type" : "text"
},
"stock" : {
"type" : "integer"
}
}
}'
1.项目结构

2.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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.hxl</groupId>
<artifactId>springboot_es</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>springboot_es</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.12.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<!-- SpringBoot Web 核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- ES6.8 Rest客户端核心依赖 (SpringBoot2.3原生适配,无需指定版本) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- lombok 简化实体类 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<optional>true</optional>
</dependency>
<!-- fastjson 实体类转JSON字符串 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.32</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- ========== 新增:手动指定内嵌Tomcat依赖,解决JDK21下Web容器工厂缺失 ========== -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
<build>
<plugins>
<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>
</build>
</project>
3.配置文件application.yml
# ============ Elasticsearch 6.8 核心配置 (带账号密码+Restful方式) ============
elasticsearch:
# ES的Rest地址,集群用英文逗号分隔,格式:http://ip:9200
rest-addresses: http://192.168.1.134:19200
# ES账号(ES6.8默认超级管理员账号:elastic)
username: elastic
# ES密码(你的ES6.8设置的密码)
password: elastic
# 连接超时时间
connect-timeout: 5000
# 读取超时时间
socket-timeout: 30000
4.各java文件
ElasticsearchConfig.java
package org.hxl.config;
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.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* ES6.8 RestHighLevelClient 配置类
* 方式:Restful HTTP 9200端口 + 账号密码认证,生产环境推荐方案
*/
@Configuration
public class ElasticsearchConfig {
@Value("${elasticsearch.rest-addresses}")
private String restAddresses;
@Value("${elasticsearch.username}")
private String username;
@Value("${elasticsearch.password}")
private String password;
@Value("${elasticsearch.connect-timeout}")
private int connectTimeout;
@Value("${elasticsearch.socket-timeout}")
private int socketTimeout;
/**
* 注入带账号密码的RestHighLevelClient核心客户端
*/
@Bean
public RestHighLevelClient restHighLevelClient() {
// 1. 账号密码认证核心配置
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
// 2. 解析ES地址
String[] addressArr = restAddresses.split(",");
HttpHost[] httpHosts = new HttpHost[addressArr.length];
for (int i = 0; i < addressArr.length; i++) {
String address = addressArr[i].replace("http://", "");
String[] hostAndPort = address.split(":");
String host = hostAndPort[0];
int port = Integer.parseInt(hostAndPort[1]);
httpHosts[i] = new HttpHost(host, port, "http");
}
// 3. 创建客户端并配置账号密码+超时时间
RestClientBuilder builder = RestClient.builder(httpHosts)
.setHttpClientConfigCallback(httpClientBuilder -> {
httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
return httpClientBuilder;
})
.setRequestConfigCallback(requestConfigBuilder -> {
requestConfigBuilder.setConnectTimeout(connectTimeout);
requestConfigBuilder.setSocketTimeout(socketTimeout);
return requestConfigBuilder;
});
return new RestHighLevelClient(builder);
}
}
ProductController.java
package org.hxl.controller;
import org.hxl.entity.Product;
import org.hxl.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/es/product")
public class ProductController {
@Autowired
private ProductService productService;
/**
* 新增商品
*/
@PostMapping("/add")
public ResponseEntity<String> add(@RequestBody Product product) throws IOException {
boolean flag = productService.addProduct(product);
System.out.println(flag);
return flag ? ResponseEntity.ok("新增成功") : ResponseEntity.ok("新增失败");
}
/**
* 根据ID查询商品
*/
@GetMapping("/get/{id}")
public ResponseEntity<String> getById(@PathVariable String id) throws IOException {
String product = productService.getProductById(id);
return ResponseEntity.ok(product);
}
/**
* 修改商品
*/
@PostMapping("/update/{id}")
public ResponseEntity<String> update(@PathVariable String id, @RequestBody Map<String, Object> updateMap) throws IOException {
boolean flag = productService.updateProduct(id, updateMap);
return flag ? ResponseEntity.ok("修改成功") : ResponseEntity.ok("修改失败");
}
/**
* 删除商品
*/
@GetMapping("/delete/{id}")
public ResponseEntity<String> delete(@PathVariable String id) throws IOException {
boolean flag = productService.deleteProduct(id);
return flag ? ResponseEntity.ok("删除成功") : ResponseEntity.ok("删除失败");
}
/**
* 批量新增商品
*/
@PostMapping("/batchAdd")
public ResponseEntity<String> batchAdd(@RequestBody List<Map<String, Object>> productList) throws IOException {
boolean flag = productService.batchAddProduct(productList);
return flag ? ResponseEntity.ok("批量新增成功") : ResponseEntity.ok("批量新增失败");
}
/**
* 商品名称分词搜索+分页
*/
@GetMapping("/search")
public ResponseEntity<List<String>> search(
@RequestParam String keyword,
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) throws IOException {
List<String> list = productService.searchProduct(keyword, pageNum, pageSize);
return ResponseEntity.ok(list);
}
}
Product.java
package org.hxl.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 商品实体类,对应ES的product索引文档
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
// 商品ID,对应ES文档的_id
private String id;
// 商品名称-分词查询
private String productName;
// 商品分类-不分词精准匹配
private String category;
// 商品价格
private Double price;
// 商品库存
private Integer stock;
// 商品描述-分词查询
private String description;
}
ProductServiceImpl.java
package org.hxl.service.impl;
import org.hxl.entity.Product;
import org.hxl.service.ProductService;
import org.hxl.util.EsRestUtil;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@Service
public class ProductServiceImpl implements ProductService {
// 固定ES6.8的索引名和类型名
private static final String ES_INDEX = "product";
private static final String ES_TYPE = "product_type";
@Autowired
private EsRestUtil esRestUtil;
@Override
public boolean addProduct(Product product) throws IOException {
return esRestUtil.addDoc(ES_INDEX, ES_TYPE, product.getId(), product);
}
@Override
public String getProductById(String id) throws IOException {
return esRestUtil.getDocById(ES_INDEX, ES_TYPE, id);
}
@Override
public boolean updateProduct(String id, Map<String, Object> updateMap) throws IOException {
return esRestUtil.updateDoc(ES_INDEX, ES_TYPE, id, updateMap);
}
@Override
public boolean deleteProduct(String id) throws IOException {
return esRestUtil.deleteDocById(ES_INDEX, ES_TYPE, id);
}
@Override
public boolean batchAddProduct(List<Map<String, Object>> productList) throws IOException {
return esRestUtil.batchAddDoc(ES_INDEX, ES_TYPE, productList);
}
@Override
public List<String> searchProduct(String keyword, int pageNum, int pageSize) throws IOException {
// 商品名称分词匹配,ES6.8标准语法
MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("productName", keyword);
return esRestUtil.searchDoc(ES_INDEX, ES_TYPE, queryBuilder, pageNum, pageSize);
}
}
ProductService.java
package org.hxl.service;
import org.hxl.entity.Product;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface ProductService {
// 新增商品
boolean addProduct(Product product) throws IOException;
// 根据ID查询商品
String getProductById(String id) throws IOException;
// 根据ID修改商品
boolean updateProduct(String id, Map<String, Object> updateMap) throws IOException;
// 根据ID删除商品
boolean deleteProduct(String id) throws IOException;
// 批量新增商品
boolean batchAddProduct(List<Map<String, Object>> productList) throws IOException;
// 商品名称分词搜索+分页
List<String> searchProduct(String keyword, int pageNum, int pageSize) throws IOException;
}
EsRestUtil.java
package org.hxl.util;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
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.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
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;
/**
* ES6.8 REST客户端通用工具类
* ? 适配:JDK21 + SpringBoot2.3 + 账号密码认证
* ? 全坑修复:参数缺失+序列化异常+JDK21兼容
* ? ES6.8规则:index + type 必须同时指定
*/
@Slf4j
@Component
public class EsRestUtil {
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 新增文档
*/
public boolean addDoc(String index, String type, String docId, Object data) throws IOException {
IndexRequest request = new IndexRequest(index, type, docId);
request.source(JSON.toJSONString(data), XContentType.JSON);
IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
return "CREATED".equals(response.getResult().name()) || "UPDATED".equals(response.getResult().name());
}
/**
* 根据ID修改文档
*/
public boolean updateDoc(String index, String type, String docId, Map<String, Object> updateMap) throws IOException {
UpdateRequest request = new UpdateRequest(index, type, docId);
request.doc(JSON.toJSONString(updateMap), XContentType.JSON);
UpdateResponse response = restHighLevelClient.update(request, RequestOptions.DEFAULT);
return response.status().getStatus() == 200;
}
/**
* 根据ID查询文档
*/
public String getDocById(String index, String type, String docId) throws IOException {
GetRequest request = new GetRequest(index, type, docId);
GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
return response.isExists() ? response.getSourceAsString() : null;
}
/**
* 根据ID删除文档
*/
public boolean deleteDocById(String index, String type, String docId) throws IOException {
DeleteRequest request = new DeleteRequest(index, type, docId);
DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
return response.status().getStatus() == 200;
}
/**
* 批量新增文档
*/
public boolean batchAddDoc(String index, String type, List<Map<String, Object>> dataList) throws IOException {
BulkRequest bulkRequest = new BulkRequest();
dataList.forEach(map -> {
String docId = map.get("id").toString();
bulkRequest.add(new IndexRequest(index, type, docId)
.source(JSON.toJSONString(map), XContentType.JSON));
});
BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
return !bulkResponse.hasFailures();
}
/**
* 条件分页查询文档 (ES6.8核心查询方式)
*/
public List<String> searchDoc(String index, String type, QueryBuilder queryBuilder, int pageNum, int pageSize) throws IOException {
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.types(type); // ES6.8必须指定type
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(queryBuilder);
sourceBuilder.from((pageNum - 1) * pageSize);
sourceBuilder.size(pageSize);
searchRequest.source(sourceBuilder);
SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = response.getHits().getHits();
List<String> resultList = new ArrayList<>();
for (SearchHit hit : hits) {
resultList.add(hit.getSourceAsString());
}
return resultList;
}
}
App.java
package org.hxl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* 修复点1:继承 SpringBootServletInitializer 解决Web容器工厂Bean缺失问题
* 修复点2:保留原有JDK21+ES6.8的核心配置,缺一不可
* 修复点3:@SpringBootApplication 注解保留,自动扫描包
*/
@SpringBootApplication
public class App {
public static void main(String[] args) {
// 1. 解决ES6.8 netty冲突 必加
//System.setProperty("es.set.netty.runtime.available.processors", "false");
// 2. 解决JDK21反射访问限制 必加
//System.setProperty("jdk.reflect.useDirectMethodHandle", "false");
// 3. 启动项目
SpringApplication.run(App.class, args);
System.out.println("===== Spring Boot 启动成功 =====");
}
}
5.调用
http://localhost:8080/es/product/add

请求报文
{
"id": "1005",
"productName": "小米15 骁龙8Gen4",
"category": "手机",
"price": 5299.0,
"stock": 5000,
"description": "澎湃OS系统"
}
es里查看
curl -u elastic:elastic -H "Content-Type: application/json" -XGET '192.168.1.134:19200/product/_search?pretty' -d '
{
"query": { "match_all": {} }
}'
浙公网安备 33010602011771号