以下是针对 Spring Boot 2.x 版本集成 Elasticsearch 的完整实现方案,重点适配 Spring Boot 2.x 与 Elasticsearch 7.x 的版本兼容性及配置差异:
一、版本兼容性说明
Spring Boot 2.x 与 Elasticsearch 的版本对应关系如下(关键版本):
| Spring Boot | Spring Data Elasticsearch | Elasticsearch |
|---|---|---|
| 2.3.x | 4.0.x | 7.6.x |
| 2.4.x | 4.1.x | 7.9.x |
| 2.7.x | 4.4.x | 7.17.x |
推荐组合:Spring Boot 2.7.x + Elasticsearch 7.17.x + Spring Data Elasticsearch 4.4.x
(若需更低版本,如 Elasticsearch 6.x,需降级 Spring Data Elasticsearch 至 3.x)
二、依赖配置(Maven)
在 pom.xml 中添加以下依赖(以 Spring Boot 2.7.x 为例):
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Data Elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- IK 分词器(中文分词) -->
<dependency>
<groupId>com.github.wensiqun</groupId>
<artifactId>elasticsearch-analysis-ik</artifactId>
<version>7.17.0</version>
</dependency>
</dependencies>
三、配置文件(application.yml)
spring:
elasticsearch:
rest:
uris: http://localhost:9200 # Elasticsearch 集群地址
username: elastic # 用户名(默认 elastic)
password: changeme # 密码(默认 changeme)
connection-timeout: 5s # 连接超时时间
read-timeout: 30s # 读取超时时间
四、实体类映射
1. 基础实体类(带中文分词)
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import lombok.Data;
@Data
@Document(indexName = "products", createIndex = false) // 手动创建索引
public class Product {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String name; // 中文全文检索
@Field(type = FieldType.Keyword)
private String category; // 精确匹配
@Field(type = FieldType.Double)
private Double price;
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createTime;
}
2. 索引创建(手动执行)
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.stereotype.Component;
@Component
public class IndexInitializer {
private final ElasticsearchRestTemplate elasticsearchTemplate;
public IndexInitializer(ElasticsearchRestTemplate elasticsearchTemplate) {
this.elasticsearchTemplate = elasticsearchTemplate;
}
public void createIndex() {
if (!elasticsearchTemplate.indexExists(Product.class)) {
elasticsearchTemplate.createIndex(Product.class);
elasticsearchTemplate.putMapping(Product.class);
}
}
}
五、Repository 接口
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface ProductRepository extends ElasticsearchRepository<Product, String> {
// 按分类查询(自动生成)
Page<Product> findByCategory(String category, Pageable pageable);
// 模糊查询(名称包含关键字)
Page<Product> findByNameContaining(String keyword, Pageable pageable);
// 复杂查询(自定义方法)
@Query("{"bool": {"must": [{"match": {"name": "?0"}}], "filter": [{"range": {"price": {"gte": ?1, "lte": ?2}}}]}}")
Page<Product> searchWithFilters(String name, Double minPrice, Double maxPrice, Pageable pageable);
}
六、服务层实现
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
// 分页查询所有商品
public Page<Product> getAllProducts(int page, int size) {
return repository.findAll(PageRequest.of(page, size));
}
// 高级查询(名称+价格范围)
public Page<Product> advancedSearch(String keyword, Double minPrice, Double maxPrice, int page, int size) {
Query query = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("name", keyword))
.filter(QueryBuilders.rangeQuery("price").gte(minPrice).lte(maxPrice)))
.withPageable(PageRequest.of(page, size))
.build();
return repository.search(query);
}
}
七、控制器层
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/products")
public class ProductController {
private final ProductService service;
public ProductController(ProductService service) {
this.service = service;
}
// 查询所有商品(分页)
@GetMapping
public Page<Product> getAllProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return service.getAllProducts(page, size);
}
// 高级搜索
@PostMapping("/search")
public Page<Product> searchProducts(
@RequestBody SearchRequest request) {
return service.advancedSearch(
request.getKeyword(),
request.getMinPrice(),
request.getMaxPrice(),
request.getPage(),
request.getSize());
}
}
八、版本适配注意事项
-
依赖冲突
-
Spring Boot 2.x 默认使用 Elasticsearch 7.x 客户端,若需连接 Elasticsearch 6.x,需显式指定低版本客户端:
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.10.0</version> </dependency>
-
-
分词器安装
-
需在 Elasticsearch 中安装 IK 分词器插件:
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.17.0/elasticsearch-analysis-ik-7.17.0.zip
-
-
日期格式
-
Elasticsearch 7.x 默认日期格式为
strict_date_optional_time,需在实体类中显式指定格式:@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd HH:mm:ss")
-
九、常见问题解决
| 问题现象 | 解决方案 |
|---|---|
NoNodeAvailableException | 检查 Elasticsearch 服务状态及网络连通性 |
| 中文分词失效 | 确认 IK 分词器已正确安装,重启 Elasticsearch |
| 字段映射不匹配 | 删除旧索引后重建:elasticsearchTemplate.deleteIndex(Product.class) |
通过以上配置,Spring Boot 2.x 可无缝对接 Elasticsearch 7.x,支持从基础查询到复杂聚合的全场景需求。实际开发中需根据业务需求调整索引分片、副本数及查询策略。