整合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,构建高效的搜索和分析功能。