整合Jdk17+Spring Boot3.2+Elasticsearch9.0+mybatis3.5.12的简单用法
Elasticsearch是一个基于Lucene的分布式搜索和分析引擎,广泛应用于全文搜索、日志分析等场景。结合Spring Boot可以快速构建强大的搜索应用。本文将介绍如何在Spring Boot项目中集成和使用Elasticsearch。
ES9.0.1目前支持的包只有
elasticsearch-rest-client/ - - elasticsearch-rest-client-sniffer/
注意:9.0版本暂不支持elasticsearch-rest-high-level-client
所以我们要用elasticsearch-rest-client来进行开发。
一、环境准备
1. 添加依赖
首先,在Spring Boot项目的`pom.xml`中添加必要的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<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.example</groupId><artifactId>EsExample</artifactId><version>1.0-SNAPSHOT</version><properties><java.version>17</java.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><mybatisplus.version>3.5.12</mybatisplus.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId></dependency><!-- <!– MyBatis集成 –>-->
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>${mybatis.version}</version>-->
<!-- </dependency>--><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.23</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId><version>3.3.4</version></dependency><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><version>3.1.6</version></dependency><dependency><groupId>org.hibernate.orm</groupId><artifactId>hibernate-core</artifactId><version>6.2.5.Final</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-jsqlparser-4.9</artifactId><version>${mybatisplus.version}</version></dependency><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>9.0.1</version></dependency><dependency><groupId>co.elastic.clients</groupId><artifactId>elasticsearch-java</artifactId><version>9.0.1</version></dependency><dependency><groupId>jakarta.json</groupId><artifactId>jakarta.json-api</artifactId><version>2.1.3</version></dependency><dependency><groupId>com.fasterxml.jackson.datatype</groupId><artifactId>jackson-datatype-jsr310</artifactId><version>2.16.0</version></dependency><dependency><groupId>org.testcontainers</groupId><artifactId>elasticsearch</artifactId><version>1.21.0</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-compress</artifactId><version>1.21</version></dependency><dependency><groupId>org.testcontainers</groupId><artifactId>testcontainers</artifactId><version>1.21.0</version></dependency><dependency><groupId>com.github.slugify</groupId><artifactId>slugify</artifactId><version>3.0.6</version></dependency>
<!-- <dependency>-->
<!-- <groupId>org.apache.httpcomponents</groupId>-->
<!-- <artifactId>httpclient</artifactId>-->
<!-- <version>4.5.13</version>-->
<!-- </dependency>--><dependency><groupId>jakarta.json.bind</groupId><artifactId>jakarta.json.bind-api</artifactId><version>3.0.1</version></dependency><dependency><groupId>org.eclipse</groupId><artifactId>yasson</artifactId><version>3.0.4</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>3.2.0</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>${mybatisplus.version}</version></dependency></dependencies></dependencyManagement><repositories><repository><id>aliyun</id><url>https://maven.aliyun.com/nexus/content/groups/public</url></repository><repository><id>oss-public</id><url>https://oss.sonatype.org/content/repositories/public</url></repository><repository><id>snapshots</id><url>https://central.sonatype.com/repository/maven-snapshots/</url><releases><enabled>false</enabled></releases><snapshots><enabled>true</enabled></snapshots></repository></repositories>
</project>
2. 配置Elasticsearch连接
在 application.yml 中配置Elasticsearch连接,pgsql连接:
Elasticsearch配置
spring:datasource:driver-class-name: org.postgresql.Driverurl: jdbc:postgresql://localhost:5432/postgres?stringtype=unspecifiedusername: postgrespassword: 123456jpa:hibernate:ddl-auto: update #自动生成数据库表properties:hibernate:dialect: org.hibernate.dialect.PostgreSQLDialectshow-sql: true # jpa配置,在控制台显示hibernate的sqlserver:port: 8904servlet:encoding:charset: UTF-8elasticsearch:server:url: https://localhost:9200api:key: YL5i65YB6ixG4DLbphIWuserName: elasticpassword: _XRuyepioTeZGzQOhUfkhost: localhostport: 9200logging:level:org.example.mapper: debugfile:path: D://testaa/img/
二、基本用法
1. 定义实体类
使用 Table 来注解标记表名称以及es的索引:
@Id注解用于es的id
/*** 测试日期** @author lyl* @version v1.0* @since 2025/5/26*/
@Data
@Table(name="sys_base_data")
@TableName("sys_base_data")
public class StockBaseEntity implements Serializable {@Idprivate String id;private String name;@TableField(fill= FieldFill.INSERT)@JsonFormat(shape= JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd",timezone = "GMT+8")private String createDate;@TableField(fill= FieldFill.INSERT)@JsonFormat(shape=JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")private Date updateDate;
}
2. 创建ES连接对象
Spring Data Elasticsearch提供了类似于JPA的Repository接口:
package org.example.es.service;import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.message.BasicHeader;
import org.elasticsearch.client.RestClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;@Configuration
public class ElasticClient {@Value("${elasticsearch.server.url}")private String serverUrl;@Value("${elasticsearch.api.key}")private String apiKey;@Value("${elasticsearch.host}")private String host;@Value("${elasticsearch.port}")private int port;@Value("${elasticsearch.userName}")private String userName;@Value("${elasticsearch.password}")private String password;@AutowiredResourceLoader resourceLoader;@Beanpublic ElasticsearchClient elasticRestClient() throws IOException {// ES服务器URL// Connection settingstry {HttpHost httphost = new HttpHost(host, port, "https");SSLContext sslContext = createInsecureSSLContext();BasicCredentialsProvider credsProv = new BasicCredentialsProvider();credsProv.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password));// Building the rest clientRestClient restClient = RestClient.builder(httphost).setHttpClientConfigCallback(hc -> hc.setDefaultCredentialsProvider(credsProv).setSSLContext(sslContext)).build();ObjectMapper mapper = JsonMapper.builder().addModule(new JavaTimeModule()).build();ElasticsearchTransport transport = new RestClientTransport(restClient,new JacksonJsonpMapper(mapper));ElasticsearchClient esClient = new ElasticsearchClient(transport);// Creating the indexes// createSimpleIndex(esClient, USERS);return esClient;} catch (NoSuchAlgorithmException e) {throw new RuntimeException(e);} catch (KeyManagementException e) {throw new RuntimeException(e);}}/*** 禁用ssl验证** @return* @throws NoSuchAlgorithmException* @throws KeyManagementException*/public SSLContext createInsecureSSLContext() throws NoSuchAlgorithmException, KeyManagementException {SSLContext sslContext = SSLContext.getInstance("TLS");sslContext.init(null, new TrustManager[]{new X509TrustManager() {public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];}}}, new SecureRandom());return sslContext;}private void createSimpleIndex(ElasticsearchClient esClient, String index) throws IOException {BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));if (!indexRes.value()) {esClient.indices().create(c -> c.index(index));}}private void createIndexWithDateMapping(ElasticsearchClient esClient, String index) throws IOException {BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));if (!indexRes.value()) {esClient.indices().create(c -> c.index(index).mappings(m -> m.properties("createdAt", p -> p.date(d -> d)).properties("updatedAt", p -> p.date(d -> d))));}}
}
3. 创建通用的操作,让所有Reposity继承此类
package org.example.es.service;import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Refresh;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.transport.endpoints.BooleanResponse;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.example.es.util.TableInfoParamUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;/*** 业务实现类封装** @author lyl* @version v1.0* @since 2025/5/21*/
@Slf4j
public class EsService<T> {public final ElasticsearchClient esClient;/*** 索引字段,自动获取T对象里@Table注解字段名*/public String index;private Class<T> clazz;/*** 获取当前类对象** @return*/
// private Class<T> getClazz() {
// return (Class<T>) GenericTypeUtils.resolveTypeArguments(getClass(), EsService.class)[0];
// }public EsService(ElasticsearchClient esClient, Class<T> clazz) {this.esClient = esClient;// 获取当前类对应的数据库表名TableInfoParamUtil tableInfoParamUtil = new TableInfoParamUtil();//Class<T> clazz = getClazz();this.clazz = clazz;this.index = tableInfoParamUtil.getTableName(clazz);}/*** 列表** @param params 参数列表,key为数据库字段名* @return*/public List<T> list(Map<String, Object> params) {Class<T> clazz = this.clazz;try {List<Query> conditions = TableInfoParamUtil.changeMapParam(params);Query query = new Query.Builder().bool(b -> b.should(conditions)).build();SearchResponse<T> response = esClient.search(s -> s.index(index).query(query), clazz);return response.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());} catch (IOException e) {throw new RuntimeException(e);}}/*** 分页查询** @param params 查询参数* @param pageNum 当前页码* @param pageSize 每页条数* @param sortField 排序字段* @return*/public IPage<T> page(Map<String, Object> params, int pageNum, int pageSize, String sortField) {int offset = (pageNum - 1) * pageSize;List<Query> conditions = TableInfoParamUtil.changeMapParam(params);Query query = new Query.Builder().bool(b -> b.should(conditions)).build();try {SearchResponse<T> getArticle = esClient.search(ss -> ss.index(index).size(pageSize) // how many results to return.from(offset) // starting point.query(query).sort(srt -> srt.field(fld -> fld.field(sortField).order(SortOrder.Desc))) // last updated first, this.clazz);if (getArticle.hits().hits().size() > 0) {List<T> collect = getArticle.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());IPage<T> page = new Page<>(pageNum, pageSize);page.setRecords(collect);page.setTotal(getArticle.hits().total().value());return page;}} catch (IOException e) {log.error(e.getMessage());}return null;}/*** 保存** @param nodeDocument*/public void save(T nodeDocument) {IndexRequest<T> articleReq = IndexRequest.of((id -> id.index(index).id(TableInfoParamUtil.getIdValue(nodeDocument)).refresh(Refresh.WaitFor).document(nodeDocument)));try {esClient.index(articleReq).id();} catch (IOException e) {log.error(e.getMessage());}}/*** 批量保存** @param nodeList*/public boolean saveBatch(List<T> nodeList) {try {BulkRequest.Builder brBuilder = new BulkRequest.Builder();for (T product : nodeList) {brBuilder.operations(b -> b.index(idx -> idx.index(index).id(TableInfoParamUtil.getIdValue(product)).document(product)));}BulkRequest bulkRequest = brBuilder.build();BulkResponse response = esClient.bulk(bulkRequest);if (!response.errors()) {return true;}} catch (IOException e) {log.error(e.getMessage());}return false;}/*** 根据id删除数据** @param id*/public void delete(String id) {try {DeleteResponse deleteArticle = esClient.delete(d -> d.index(index).id(id));if (!deleteArticle.result().name().equals("deleted")) {throw new RuntimeException("Failed to delete article");}} catch (IOException e) {throw new RuntimeException(e);}}/*** 更新** @param nodeDocument*/public void update(T nodeDocument, String id) {try {Class<T> cla = (Class<T>) nodeDocument.getClass();boolean exists = esClient.exists(b -> b.index(index).id(id)).value();if (exists) {System.out.println("exists");UpdateResponse<T> upArticle = esClient.update(up -> up.index(index).id(id).doc(nodeDocument), cla);if (!upArticle.result().name().toLowerCase().equals("updated")) {throw new RuntimeException("Article update failed");}}} catch (IOException e) {throw new RuntimeException(e);}}/*** 根据id查询** @param id 对象id* @return*/public T getById(String id) {try {GetResponse<T> searchResponse = esClient.get(ss -> ss.index(index).id(id), this.clazz);if (searchResponse.source() == null) {return null;}return searchResponse.source();} catch (IOException e) {throw new RuntimeException(e);}}/*** 获取总数** @return*/public long getCount(Map<String, Object> params) {try {SearchResponse<T> searchResponse = null;if (null != params && params.size() > 0) {List<Query> conditions = TableInfoParamUtil.changeMapParam(params);Query query = new Query.Builder().bool(b -> b.should(conditions)).build();searchResponse = esClient.search(ss -> ss.index(index).size(0).query(query), this.clazz);} else {//查全部searchResponse = esClient.search(ss -> ss.index(index).size(0).query(q -> q.matchAll(m -> m)), this.clazz);}return searchResponse.hits().total().value();} catch (IOException e) {throw new RuntimeException(e);}}public void createIndex(){try {BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));if (!indexRes.value()) {esClient.indices().create(c -> c.index(index));}} catch (IOException e) {throw new RuntimeException(e);}}public void deleteIndex(){try {BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));if (indexRes.value()) {esClient.indices().delete(d -> d.index(index));}} catch (IOException e) {throw new RuntimeException(e);}}public void createMapping() {try {BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));if (indexRes.value()) {esClient.indices().delete(d->d.index(index));esClient.indices().create(p -> p.index(index).mappings(m -> m.properties("createDate", pp -> pp.date(d -> d.format("yyyy-MM-dd"))).properties("updateDate",pp->pp.date(d->d.format("yyyy-MM-dd HH:mm:ss")))));}} catch (IOException e) {throw new RuntimeException(e);}}}
从实体对象分析es的索引和id自动注入。
package org.example.es.util;import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import jakarta.persistence.Id;
import jakarta.persistence.Table;import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** 参数转换工具类** @author lyl* @version v1.0* @since 2025/5/21*/
public class TableInfoParamUtil {/*** 查询参数封装* @param params* @return*/public static List<Query> changeMapParam(Map<String, Object> params) {List<Query> conditions = new ArrayList<>();params.forEach((k, v) -> {Query query = null;if (v instanceof String) {String value = v.toString();query = new MatchQuery.Builder().field(k).query(value).build()._toQuery();} else if (v instanceof Number) {query = new MatchQuery.Builder().field(k).query(Long.valueOf(v.toString())).build()._toQuery();} else if (v instanceof Boolean) {query = new MatchQuery.Builder().field(k).query(Boolean.valueOf(v.toString())).build()._toQuery();} else if (v instanceof Integer) {query = new MatchQuery.Builder().field(k).query(Integer.valueOf(v.toString())).build()._toQuery();} else if (v instanceof Float) {query = new MatchQuery.Builder().field(k).query(Float.valueOf(v.toString())).build()._toQuery();} else if (v instanceof Double) {query = new MatchQuery.Builder().field(k).query(Double.valueOf(v.toString())).build()._toQuery();}if (null != query) {conditions.add(query);}});return conditions;}/*** 获取表名,分析@Table注解* @param clazz* @return*/public String getTableName(Class<?> clazz) {// 检查类是否有 Table 注解if (clazz.isAnnotationPresent(Table.class)) {// 获取注解实例Table tableAnnotation = clazz.getAnnotation(Table.class);// 读取注解的属性值String tableName = tableAnnotation.name();return tableName;}return null;}/*** 获取主键值* @param entity* @return*/public static <T> String getIdValue(T entity) {Class<?> clazz = entity.getClass();for (Field field : clazz.getDeclaredFields()) {if (field.isAnnotationPresent(Id.class)) {field.setAccessible(true); // 允许访问私有字段try {String id=String.valueOf(field.get(entity));return id; // 获取字段值} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}return null;}
}
业务类实现EsService接口,除了基本增加,修改,删除,查询外,编写自定义查询方法。
package org.example.es.service;import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.RangeRelation;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import org.example.es.entity.StockBaseEntity;
import org.example.es.util.TableInfoParamUtil;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;/*** 业务实现类** @author lyl* @version v1.0* @since 2025/5/26*/
@Service
public class StockBaseResposity extends EsService<StockBaseEntity> {/*** 获取当前类对象** @param esClient* @param clazz* @return*/@Autowiredpublic StockBaseResposity(ElasticsearchClient esClient) {super(esClient, StockBaseEntity.class);}/*** 把某一年的数据按7天分组* @return*/public List<StockBaseEntity> search(){try {Query byMaxPrice = RangeQuery.of(r -> r.date(n -> n.field("createDate").gte("2024-09-01").lte("2025-04-01").format("yyyy-MM-dd")))._toQuery();Query dateDistanceFeatureQuery = Query.of(q -> q.bool(b -> b.must(m -> m.matchAll(mm -> mm)).should(sh -> sh.distanceFeature(df -> df.date(d -> d.field("createDate").pivot(Time.of(t -> t.time("7d"))).origin("now"))))));//根据条件查询相应的数据Query query1 = Query.of(q->q.bool(b->b.must(byMaxPrice)));SearchResponse<StockBaseEntity> response = esClient.search(s -> s.index(index).query(dateDistanceFeatureQuery).aggregations("dd",t->t.aggregations("day",a->a.dateHistogram(d->d.field("createDate").format("yyyy")))).size(100), StockBaseEntity.class);return response.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());} catch (IOException e) {throw new RuntimeException(e);}}}
三、高级查询
1. 使用
// 聚合查询示例
public List<StockBaseEntity> search(){try {Query dateDistanceFeatureQuery = Query.of(q -> q.bool(b -> b.must(m -> m.matchAll(mm -> mm)).should(sh -> sh.distanceFeature(df -> df.date(d -> d.field("createDate").pivot(Time.of(t -> t.time("7d"))).origin("now"))))));//查某一年的数据SearchResponse<StockBaseEntity> response = esClient.search(s -> s.index(index).query(dateDistanceFeatureQuery).aggregations("dd",t->t.aggregations("day",a->a.dateHistogram(d->d.field("createDate").format("yyyy")))).size(100), StockBaseEntity.class);return response.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());} catch (IOException e) {throw new RuntimeException(e);}}
四、索引管理
1. 创建索引
public void createIndex(){try {BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));if (!indexRes.value()) {esClient.indices().create(c -> c.index(index));}} catch (IOException e) {throw new RuntimeException(e);}}
2. 索引映射管理
可以在实体类中使用注解定义更详细的映射:
Elasticsearch 默认支持的日期格式是 strict_date_optional_time,它支持以下形式的日期时间字符串:
yyyy-MM-dd
yyyy-MM-dd HH:mm:ss
yyyy-MM-dd HH:mm:ss.SSS
例如:
2023-10-01
2023-10-01 12:30:45
2023-10-01 12:30:45.123
如果你需要使用其他日期格式,可以在字段映射(mapping)中自定义日期格式。例如:
public void createMapping() {try {BooleanResponse indexRes = esClient.indices().exists(ex -> ex.index(index));if (indexRes.value()) {
// esClient.indices().delete(d->d.index(index));esClient.indices().create(p -> p.index(index).mappings(m -> m.properties("createDate", pp -> pp.date(d -> d.format("yyyy-MM-dd"))).properties("updateDate",pp->pp.date(d->d.format("yyyy-MM-dd HH:mm:ss")))));}} catch (IOException e) {throw new RuntimeException(e);}}
然后在resources目录下创建对应的settings和mappings文件。
五、性能优化
1. 批量操作:使用`bulk`API进行批量索引
/*** 批量保存** @param nodeList*/public boolean saveBatch(List<T> nodeList) {try {BulkRequest.Builder brBuilder = new BulkRequest.Builder();for (T product : nodeList) {brBuilder.operations(b -> b.index(idx -> idx.index(index).id(TableInfoParamUtil.getIdValue(product)).document(product)));}BulkRequest bulkRequest = brBuilder.build();BulkResponse response = esClient.bulk(bulkRequest);if (!response.errors()) {return true;}} catch (IOException e) {log.error(e.getMessage());}return false;}
七、总结
Spring Boot与Elasticsearch的集成为开发强大的搜索功能提供了便利。通过elasticsearch-rest-client,我们可以:
1. 使用熟悉的Repository模式进行基本操作
2. 利用ElasticsearchTemplate实现复杂查询
3. 轻松管理索引和映射
4. 实现全文搜索、聚合分析等高级功能
在实际项目中,应根据业务需求合理设计索引结构,优化查询性能,并注意数据同步策略,确保Elasticsearch中的数据与主数据源保持一致。
通过本文介绍的方法,您可以快速在Spring Boot项目中集成Elasticsearch,构建高效的搜索和分析功能。
相关文章:

整合Jdk17+Spring Boot3.2+Elasticsearch9.0+mybatis3.5.12的简单用法
Elasticsearch是一个基于Lucene的分布式搜索和分析引擎,广泛应用于全文搜索、日志分析等场景。结合Spring Boot可以快速构建强大的搜索应用。本文将介绍如何在Spring Boot项目中集成和使用Elasticsearch。 ES9.0.1目前支持的包只有 elasticsearch-rest-client/ …...
Starrocks 物化视图的实现以及在刷新期间能否读数据
背景 本司在用Starrocks做一些业务上的分析的时候,用到了物化视图,并且在高QPS的情况下,RT也没有很大的波动,所以在此研究一下Starrock的实现,以及在刷新的时候是不是原子性的 本文基于Starrocks 3.3.5 结论 Starro…...
前后端传输 Long 类型数据时(时间戳,雪花算法ID),精度丢失的根本原因
前后端传输 Long 类型数据时,精度丢失的根本原因是 JavaScript 的 Number 类型无法精确表示超过 53 位(64 位双精度浮点数)的整数,而 Java 的 Long 类型是 64 位整数。当后端返回的 Long 值超过 2^53-1(即 90071992547…...
探索容器技术:Docker与Kubernetes的实践指南
随着云计算和微服务架构的兴起,容器技术已经成为软件开发和部署的新标准。容器技术以其轻量级、可移植性和灵活性等特点,为应用程序的快速部署、扩展和管理提供了强大的支持。在众多容器技术中,Docker和Kubernetes无疑是最受欢迎的两种。本文…...

Ubuntu从0到1搭建监控平台:本地部署到公网访问实战教程Cpolar穿透与Docker部署全过程
文章目录 前言1.关于Ward2.Docker部署3.简单使用ward4.安装cpolar内网穿透5. 配置ward公网地址6. 配置固定公网地址总结 前言 IT运维人员是否常为服务器管理系统的复杂操作所困扰?当海量性能指标图表与密集预警信号同时涌现时,这种信息过载往往让专业团…...

vscode java debug terminal 中文乱码
现象 解决 快捷键 ctrl , 进入setting 配文件添加 "terminal.integrated.automationProfile.windows": {"path": "cmd","args": ["/k","chcp","65001"]}terminal 启动时,活动也改为 utf-…...

3D PDF如何制作?SOLIDWORKS MBD模板定制技巧
SOLIDWORKS制作3D PDF模版 SOLIDWORKS MBD能够帮助工程师以清晰直观的方式描述产品尺寸信息。在3D PDF文件中,用户可以自由旋转和移动视图,方便查看模型的各个尺寸细节。 本文将带您一步步学习如何使用SOLIDWORKS MBD制作专业的3D PDF模板,…...

Qt DateTimeEdit(时间⽇期的微调框)
使⽤ QDateEdit 作为⽇期的微调框. 使⽤ QTimeEdit 作为时间的微调框 使⽤ QDateTimeEdit 作为时间⽇期的微调框. 这⼏个控件⽤法⾮常相似, 我们以 QDateTimeEdit 为例进⾏介绍. QDateTimeEdit 核⼼属性 属性说明dateTime时间⽇期的值. 形如 2000/1/1 0:00:00date单纯⽇期…...

C# 类和继承(屏蔽基类的成员)
屏蔽基类的成员 虽然派生类不能删除它继承的任何成员,但可以用与基类成员名称相同的成员来屏蔽(mask) 基类成员。这是继承的主要功能之一,非常实用。 例如,我们要继承包含某个特殊方法的基类。该方法虽然适合声明它的…...
基于qt5和stk10开发的互联调试
基于qt5和stk10开发的互联调试程序 QTSTK_Test_yuanwenjian/CppIncludes/AgAsHpopPlugin.tlb , 110080 QTSTK_Test_yuanwenjian/CppIncludes/agashpopplugin.tlh , 108623 QTSTK_Test_yuanwenjian/CppIncludes/AgAttrAutomation.tlb , 11408 QTSTK_Test_yuanwenjian/CppInclude…...
matlab雷达定位仿真
一、边扫描边跟踪雷达仿真 边扫描边跟踪(BISTAR)雷达仿真是一种实时雷达信号处理的技术,用于模拟雷达系统的操作过程,特别是那些具备连续扫描能力的雷达。它的基本原理和流程可以分为以下几个步骤: (1&…...

基于vue框架的动物园饲养管理系统a7s60(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
系统程序文件列表 项目功能:饲养员,健康登记,工作进度,动物信息,进食信息,动物健康,动物医治,饲料信息,工作留言 开题报告内容 基于Vue框架的动物园饲养管理系统开题报告 一、研究背景与意义 (一)研究背景 随着城市化进程加快和公众对生…...
MySQL 索引和事务
目录 前言 一、MySQL 索引介绍 1. 索引概述 2. 索引作用 3. 索引的分类 3.1 普通索引 3.2 唯一索引 3.3 主键索引 3.4 组合索引 (最左前缀) 3.5 全文索引 (FULLTEXT) 3.6 创建索引的原则依据 3.7 查看索引 3.8 删除索引 二、MySQL 事务 1. 事务的 ACID 原则 MYS…...
BERT分类器和朴素贝叶斯分类器比较
一、核心原理对比 维度预训练模型(如BERT)朴素贝叶斯分类器模型类型深度学习模型,基于Transformer架构,通过大规模无监督预训练学习语言表示。传统机器学习模型,基于贝叶斯定理和特征条件独立假设。特征表示自动学习文本的上下文相关表示(contextual embeddings),捕捉长…...

WPS自动换行
换行前 换行后 快捷键 第一步:启用「自动换行」功能 选中目标单元格/区域:点击需要设置的单元格(或拖动选中多个单元格)。开启自动换行(3种方式任选): 快捷按钮:在顶部菜单栏点击「…...
C#面向对象核心:类继承详解
类继承是什么? 继承是面向对象编程的三大特性之一,允许新类(派生类)基于已有类(基类)进行扩展。通过继承,派生类可以“免费”获得基类的字段、方法等成员,并添加自身独有的功能。 …...

maven中的grpc编译插件protobuf-maven-plugin详解
protobuf-maven-plugin 是 Maven 中用于编译 Protocol Buffers(protobuf)文件并生成对应语言代码(如 Java、C、Python 等)的插件。在 gRPC 项目中,它常被用来生成服务端和客户端所需的代码。以下是该插件的详细解析&am…...

服务发现Nacos
目录 Nacos server 安装 注册服务到Nacos server 接口访问Nacos server中的已注册服务 Nacos控制台介绍 Nacos:一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 在分布式服务应用中,各类服务需要统一的注册、统一的管理,这个组件工具…...

社群分享:义乌|杭州电商|店群卖家,私域鱼塘运营的排单系统开源|私域鱼塘运营|返款软件开源
熟悉东哥的朋友都知道,我自己也运营一个电商社群,主要是针对玩私域|鱼塘的电商玩家。 在当前电商环境下,社群分享型电商、店群卖家及私域鱼塘运营者,面临着日益复杂的订单管理和客服调度问题。传统的人工处理不仅效率低…...

C#回调函数深度解析
文章目录 前言什么是回调函数C#中实现回调的方式委托(Delegate)事件(Event)Action和FuncPredicateAsyncCallback匿名方法和Lambda表达式 回调函数实际应用场景异步编程事件处理策略模式LINQ查询 回调函数的优缺点优点缺点 最佳实践与注意事项总结相关资源 前言 在现代软件开发…...

通义智文开源QwenLong-L1: 迈向长上下文大推理模型的强化学习
🎉 动态 2025年5月26日: 🔥 我们正式发布🤗QwenLong-L1-32B——首个采用强化学习训练、专攻长文本推理的LRM模型。在七项长文本文档问答基准测试中,QwenLong-L1-32B性能超越OpenAI-o3-mini和Qwen3-235B-A22B等旗舰LRM,…...
HTTP代理的实际用处有哪些?HTTP代理强在哪里?
嘿,小伙伴们!今天咱们来聊聊 HTTP 代理。大家可能会经常在网页上看到类似“HTTP 代理”的关键词,是不是会心生疑惑:这不就是个技术名词吗?它到底有啥用呢?别急,听我娓娓道来。 一、什么是HTTP代…...

低代码——表单生成器以form-generator为例
主要执行流程说明: 初始化阶段 : 接收表单配置对象formConf深拷贝配置,初始化表单数据和验证规则处理每个表单组件的默认值和特殊配置(如文件上传) 渲染阶段 : 通过render函数创建el-form根组件递归渲染表…...
【Vue Vapor Mode :技术突破与性能优化的可能性】
Vue Vapor Mode :技术突破与性能优化的可能性 前言 作为一名有着Vue 2经验和Vue 3经验的开发者,你一定深刻体会过Vue从Options API到Composition API的演进,也感受过Vue 3在性能上相比Vue 2的显著提升。现在,Vue团队正在开发一个…...
Parasoft C++Test软件单元测试_常见问题及处理
系列文章目录 Parasoft C++Test软件静态分析:操作指南(编码规范、质量度量)、常见问题及处理 Parasoft C++Test软件单元测试:操作指南、实例讲解、常见问题及处理 Parasoft C++Test软件集成测试:操作指南、实例讲解、常见问题及处理 进阶扩展:自动生成静态分析文档、自动…...
vue渲染数组各子项实现文本超出宽度显示tooltip
vue渲染数组各子项实现文本超出宽度显示tooltip 需求背景 vue项目中,通过v-for渲染数组,子项中均存在一行描述文字。当描述文字超出固定宽度时,显示省略号并且鼠标悬浮时显示tooltip;当描述文字不超出固定宽度时则正常显示&…...
libreoffice容器word转pdf
先说结论,市面上不花钱的,简单的效果好的就是这个种方式,在线测试下来不如命令转的效果好。AsposeWords和SpireDoc效果都不错,但是只有这个word转pdf感觉花3-5w不划算。 下载容器路径 https://docker.aityp.com/i/search?searchl…...
AI模型升级与机器人产业落地同步推进
2025年5月28日,中国AI领域迎来两项实质性进展。DeepSeek-R1模型完成小版本试升级,开源社区发布其0528版本。本次更新聚焦语义理解精准性、复杂逻辑推理和长文本处理能力的提升。在代码测试平台Live CodeBench中,开发者反馈其编程性能已接近Op…...
安全编码与AI接口权限控制
安全编码与AI接口权限控制 在AI系统中,模型服务的开放接口往往涉及敏感数据、核心算法与算力资源,如果缺乏有效的安全编码与权限控制机制,极易引发数据泄露、滥用调用或非法操作等问题。本节将从“接口安全策略”“权限验证流程”“Token管控机制”“多租户身份隔离”四个方…...

linux centos 服务器性能排查 vmstat、top等常用指令
背景:项目上经常出现系统运行缓慢,由于数据库服务器是linux服务器,记录下linux服务器性能排查常用指令 vmstat vmstat介绍 vmstat 命令报告关于内核线程、虚拟内存、磁盘、陷阱和 CPU 活动的统计信息。由 vmstat 命令生成的报告可以用于平衡系统负载活动。系统范围内的这…...