SpringBoot集成Elasticsearch | Spring官方场景启动器(Spring Data Elasticsearch)方式

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. 测试步骤

  1. 启动ES服务器(版本7.15.2,确保9200端口可访问);
  2. 启动SpringBoot项目;
  3. 调用测试接口添加数据:访问http://localhost:8081/api/spring-data/employee/test/save?jobNo=2024001&name=张三&job=Java开发&age=28&salary=25000&jobDay=2023-01-15&remark=技术骨干
  4. 调用高亮查询接口:访问http://localhost:8081/api/spring-data/employee/search-highlight?name=张&pageNum=1&pageSize=10,返回结果中name字段会被红色span标签包裹。

相关推荐
星辰h3 小时前
基于JWT的RESTful登录系统实现
前端·spring boot·后端·mysql·restful·jwt
YDS8293 小时前
苍穹外卖 —— 文件上传和菜品的CRUD
java·spring boot·后端
Java陈序员3 小时前
完全开源!一款基于 SpringBoot + Vue 构建的社区平台!
vue.js·spring boot·github·社区
.柒宇.3 小时前
《云岚到家》第一章个人总结
spring boot·spring·spring cloud
刘一说3 小时前
深入掌握 Spring Boot Web 开发:构建高性能 RESTful API 的最佳实践
前端·spring boot·restful
摇滚侠3 小时前
Spring Boot3零基础教程,Actuator 导入,笔记82
java·spring boot·笔记
阿Y加油吧4 小时前
一篇文章深入理解Elasticsearch高级用法
elasticsearch
洛克大航海4 小时前
安装 ElasticSearch、Logstash、Kibana、Kafka 和 Filebeat
大数据·elasticsearch·kafka·kibana·logstash·filebeat
lang201509284 小时前
Spring XML AOP配置实战指南
xml·java·spring