SpringBoot集成Elasticsearch | Spring官方场景启动器(Spring Data Elasticsearch)方式
- 前言
-
- [1. 版本匹配与Maven依赖](#1. 版本匹配与Maven依赖)
- [2. 配置文件(application.yml)](#2. 配置文件(application.yml))
- [3. 核心代码实现](#3. 核心代码实现)
-
- [3.1 实体类(映射ES索引)](#3.1 实体类(映射ES索引))
- [3.2 Repository接口(CRUD基础操作)](#3.2 Repository接口(CRUD基础操作))
- [3.3 Service层(业务逻辑封装)](#3.3 Service层(业务逻辑封装))
- [3.4 Controller层(测试接口)](#3.4 Controller层(测试接口))
- [4. 测试步骤](#4. 测试步骤)
SpringBoot集成Elasticsearch的三种核心方式,
Spring官方场景启动器、
Elasticsearch 7.x专属HLRC(High Level Rest Client)
Elasticsearch 8.x专属Java Client。
前言
Spring官方场景启动器(Spring Data Elasticsearch)
Spring Data Elasticsearch是Spring对ES官方接口的二次封装,优势是集成Spring生态更便捷,缺点是版本更新滞后
1. 版本匹配与Maven依赖
xml
<dependencies>
<!-- Spring Data Elasticsearch核心依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- Web依赖(用于测试接口) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Lombok(简化代码,可选但推荐) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- FastJSON(JSON序列化,可选) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
</dependencies>
2. 配置文件(application.yml)
支持ES集群配置,7.x默认未开启安全认证,8.x需额外配置账号密码与SSL(此处以7.x为例)。
yaml
server:
port: 8081 # 服务端口
spring:
elasticsearch:
rest:
# ES服务器地址(集群用逗号分隔,如127.0.0.1:9200,127.0.0.1:9201)
uris: http://127.0.0.1:9200
# 若开启安全认证(如8.x默认开启),需添加以下配置
# username: elastic
# password: 你的ES密码
# 连接超时配置
connection-timeout: 1000ms
socket-timeout: 30000ms
3. 核心代码实现
3.1 实体类(映射ES索引)
用@Document指定索引名,@Id指定文档唯一标识,@Field指定字段类型与分词器。
环境提示:
- 开发/测试环境:可省略@Field注解部分配置,依赖ES动态映射快速验证功能;
- 生产环境:必须通过@Field精准定义字段类型、分词器等(如jobNo设为Keyword、name指定IK分词器),避免动态映射导致的类型混乱或检索异常。
java
package com.es.demo.entity;
import lombok.Data;
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 java.math.BigDecimal;
import java.util.Date;
/**
* 员工实体(映射ES索引:employee_spring_data)
*/
@Data
@Document(indexName = "employee_spring_data", shards = 3, replicas = 1)
// indexName:索引名;shards:分片数;replicas:副本数(生产环境根据集群调整)
public class EmployeeSpringData {
@Id // 对应ES文档的_id字段(唯一标识)
private String docId; // 文档ID(可自定义,也可让ES自动生成)
@Field(type = FieldType.Keyword) // Keyword:精确匹配,不分词
private String jobNo; // 工号(唯一,精确查询)
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
// Text:分词匹配;analyzer:索引时分词器;searchAnalyzer:查询时分词器(需提前安装IK分词器)
private String name; // 姓名(支持模糊查询)
@Field(type = FieldType.Keyword)
private String job; // 岗位(如Java开发)
@Field(type = FieldType.Integer)
private Integer age; // 年龄
@Field(type = FieldType.Double)
private BigDecimal salary; // 薪资
@Field(type = FieldType.Date, format = DateFormat.custom, pattern = "yyyy-MM-dd")
private Date jobDay; // 入职时间(自定义日期格式)
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String remark; // 备注
}
3.2 Repository接口(CRUD基础操作)
继承ElasticsearchRepository,Spring会自动实现基础CRUD方法,无需手动编写。
java
package com.es.demo.repository;
import com.es.demo.entity.EmployeeSpringData;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 员工ES Repository(Spring Data自动实现CRUD)
* 泛型:<实体类, 文档ID类型>
*/
@Repository
public interface EmployeeSpringDataRepository extends ElasticsearchRepository<EmployeeSpringData, String> {
// 1. 自定义查询:根据姓名模糊查询(Spring Data按方法名自动生成SQL)
// 方法名规则:findBy + 字段名 + 查询规则(Containing=模糊匹配)
List<EmployeeSpringData> findByNameContaining(String name);
// 2. 自定义查询:根据岗位和年龄范围查询
List<EmployeeSpringData> findByJobAndAgeBetween(String job, Integer minAge, Integer maxAge);
}
3.3 Service层(业务逻辑封装)
整合Repository,处理复杂查询(如高亮、分页)。
java
package com.es.demo.service;
import com.es.demo.entity.EmployeeSpringData;
import com.es.demo.repository.EmployeeSpringDataRepository;
import lombok.RequiredArgsConstructor;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor // Lombok自动注入构造器(替代@Autowired)
public class EmployeeSpringDataService {
// 注入Repository(基础CRUD)
private final EmployeeSpringDataRepository employeeRepository;
// 注入Template(复杂查询,如高亮、聚合)
private final ElasticsearchRestTemplate elasticsearchTemplate;
/**
* 1. 添加/修改文档(docId存在则修改,不存在则新增)
*/
public EmployeeSpringData save(EmployeeSpringData employee) {
return employeeRepository.save(employee);
}
/**
* 2. 批量添加文档
*/
public List<EmployeeSpringData> batchSave(List<EmployeeSpringData> employees) {
return (List<EmployeeSpringData>) employeeRepository.saveAll(employees);
}
/**
* 3. 根据docId删除文档
*/
public void deleteByDocId(String docId) {
employeeRepository.deleteById(docId);
}
/**
* 4. 根据docId查询文档
*/
public EmployeeSpringData getByDocId(String docId) {
// findById返回Optional,用orElse(null)处理不存在的情况
return employeeRepository.findById(docId).orElse(null);
}
/**
* 5. 高亮查询(姓名模糊匹配,分页)
*/
public Page<EmployeeSpringData> searchHighlight(String name, Integer pageNum, Integer pageSize) {
// 1. 构建高亮配置(红色span标签)
HighlightBuilder.Field highlightField = new HighlightBuilder.Field("name")
.preTags("<span style='color:red'>")
.postTags("</span>")
.requireFieldMatch(false); // 允许高亮多个字段
// 2. 构建原生查询(NativeSearchQuery支持ES原生DSL)
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.matchQuery("name", name)) // 姓名模糊匹配
.withHighlightFields(highlightField) // 加入高亮配置
.withPageable(PageRequest.of(pageNum - 1, pageSize)) // 分页(pageNum从0开始,需减1)
.build();
// 3. 执行查询并处理高亮结果
SearchHits<EmployeeSpringData> searchHits = elasticsearchTemplate.search(searchQuery, EmployeeSpringData.class);
// 转换为Page(符合Spring Data分页规范)
List<EmployeeSpringData> content = searchHits.stream()
.map(hit -> {
EmployeeSpringData employee = hit.getContent();
// 替换高亮字段(将原始name替换为高亮后的内容)
if (!hit.getHighlightFields().isEmpty()) {
String highlightName = hit.getHighlightFields().get("name").get(0);
employee.setName(highlightName);
}
return employee;
})
.collect(Collectors.toList());
// 构建Page对象(包含总条数、分页信息)
long total = searchHits.getTotalHits();
Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
return new org.springframework.data.domain.PageImpl<>(content, pageable, total);
}
}
3.4 Controller层(测试接口)
java
package com.es.demo.controller;
import com.es.demo.entity.EmployeeSpringData;
import com.es.demo.service.EmployeeSpringDataService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
@RestController
@RequestMapping("/api/spring-data/employee")
@RequiredArgsConstructor
public class EmployeeSpringDataController {
private final EmployeeSpringDataService employeeService;
/**
* 1. 添加/修改员工
*/
@PostMapping("/save")
public EmployeeSpringData save(@RequestBody EmployeeSpringData employee) {
return employeeService.save(employee);
}
/**
* 2. 批量添加员工
*/
@PostMapping("/batch-save")
public List<EmployeeSpringData> batchSave(@RequestBody List<EmployeeSpringData> employees) {
return employeeService.batchSave(employees);
}
/**
* 3. 根据docId删除员工
*/
@DeleteMapping("/delete/{docId}")
public String delete(@PathVariable String docId) {
employeeService.deleteByDocId(docId);
return "删除成功(docId:" + docId + ")";
}
/**
* 4. 根据docId查询员工
*/
@GetMapping("/get/{docId}")
public EmployeeSpringData get(@PathVariable String docId) {
return employeeService.getByDocId(docId);
}
/**
* 5. 高亮查询员工(分页)
*/
@GetMapping("/search-highlight")
public Page<EmployeeSpringData> searchHighlight(
@RequestParam String name,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
return employeeService.searchHighlight(name, pageNum, pageSize);
}
/**
* 6. 测试示例:添加单个员工(方便手动测试,无需传JSON)
*/
@GetMapping("/test/save")
public EmployeeSpringData testSave(
@RequestParam String jobNo,
@RequestParam String name,
@RequestParam String job,
@RequestParam Integer age,
@RequestParam BigDecimal salary,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") Date jobDay,
@RequestParam(required = false) String remark) {
EmployeeSpringData employee = new EmployeeSpringData();
employee.setJobNo(jobNo);
employee.setName(name);
employee.setJob(job);
employee.setAge(age);
employee.setSalary(salary);
employee.setJobDay(jobDay);
employee.setRemark(remark);
// docId留空,让ES自动生成
return employeeService.save(employee);
}
}
4. 测试步骤
- 启动ES服务器(版本7.15.2,确保9200端口可访问);
- 启动SpringBoot项目;
- 调用测试接口添加数据:访问
http://localhost:8081/api/spring-data/employee/test/save?jobNo=2024001&name=张三&job=Java开发&age=28&salary=25000&jobDay=2023-01-15&remark=技术骨干; - 调用高亮查询接口:访问
http://localhost:8081/api/spring-data/employee/search-highlight?name=张&pageNum=1&pageSize=10,返回结果中name字段会被红色span标签包裹。