SpringBoot集成Elasticsearch | Elasticsearch 8.x专属Java Client

SpringBoot集成Elasticsearch | Elasticsearch 8.x专属Java Client

  • 前言
    • [1. 版本说明与Maven依赖](#1. 版本说明与Maven依赖)
    • [2. 配置文件(application.yml)](#2. 配置文件(application.yml))
    • [3. 核心代码实现](#3. 核心代码实现)
      • [3.1 配置类(构建Java Client)](#3.1 配置类(构建Java Client))
      • [3.2 工具类(封装8.x Java Client操作)](#3.2 工具类(封装8.x Java Client操作))
      • [3.3 实体类(Employee8xJavaClient)](#3.3 实体类(Employee8xJavaClient))
      • [3.4 Controller层(测试接口)](#3.4 Controller层(测试接口))
    • [4. 测试步骤](#4. 测试步骤)

SpringBoot集成Elasticsearch的三种核心方式,
Spring官方场景启动器
Elasticsearch 7.x专属HLRC(High Level Rest Client)
Elasticsearch 8.x专属Java Client

前言

Elasticsearch 8.x专属Java Client

Elasticsearch 8.x官方推出全新的Elasticsearch Java Client ,替代了HLRC(HLRC在8.x中已弃用)。

它基于异步非阻塞IO,支持所有8.x新特性,但仅兼容8.x ES服务器,与7.x完全不兼容。

1. 版本说明与Maven依赖

ES 8.x Java Client需与ES服务器版本完全一致(如ES 8.14.0 → Java Client 8.14.0),且依赖结构与HLRC不同。

xml 复制代码
<dependencies>
    <!-- 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 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.83</version>
    </dependency>

    <!-- 1. ES 8.x Java Client核心依赖(版本与ES服务器一致) -->
    <dependency>
        <groupId>co.elastic.clients</groupId>
        <artifactId>elasticsearch-java</artifactId>
        <version>8.14.0</version>
    </dependency>
    <!-- 2. JSON处理器(Java Client依赖Jackson,需指定版本) -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.15.2</version>
    </dependency>
    <!-- 3. 日志依赖(避免SLF4J绑定错误) -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.36</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.36</version>
    </dependency>
</dependencies>

2. 配置文件(application.yml)

ES 8.x默认开启安全认证SSL加密,需配置账号密码、SSL信任策略(开发环境可关闭SSL验证,生产环境需配置证书)。

yaml 复制代码
server:
  port: 8083 # 与前两种方式区分端口

# ES 8.x Java Client配置
elasticsearch:
  java-client:
    cluster-name: es-cluster-8x # 集群名称(非必需)
    hosts: 127.0.0.1:9200 # 服务器地址(集群用逗号分隔)
    scheme: https # 8.x默认https(7.x默认http)
    # 安全认证(8.x默认开启,账号默认elastic,密码在ES首次启动时生成)
    username: elastic
    password: 你的ES 8.x密码
    # 超时配置(毫秒)
    connect-timeout: 5000
    socket-timeout: 30000
    # SSL配置(开发环境关闭证书验证,生产环境需配置cert-path)
    ssl:
      disable-verification: true # 关闭SSL证书验证(开发用,生产禁用)
      # cert-path: classpath:es-cert.pem # 生产环境:SSL证书路径

3. 核心代码实现

3.1 配置类(构建Java Client)

ES 8.x Java Client提供ElasticsearchClient(同步)和ElasticsearchAsyncClient(异步),此处以同步客户端为例。

java 复制代码
package com.es.demo.config;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestClientBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.security.cert.X509Certificate;

/**
 * ES 8.x Java Client配置类
 */
@Slf4j
@Data
@Configuration
@ConfigurationProperties(prefix = "elasticsearch.java-client")
public class Es8xJavaClientConfig {
    // 配置参数(与application.yml对应)
    private String clusterName;
    private String hosts;
    private String scheme;
    private String username;
    private String password;
    private int connectTimeout;
    private int socketTimeout;
    private SslConfig ssl;

    // 内部类:SSL配置
    @Data
    public static class SslConfig {
        private boolean disableVerification;
        private String certPath;
    }

    /**
     * 构建ElasticsearchClient(同步客户端,Spring单例注入)
     */
    @Bean(name = "es8xElasticsearchClient")
    public ElasticsearchClient elasticsearchClient() throws Exception {
        // 1. 解析ES服务器地址
        HttpHost[] httpHosts = parseHttpHosts();

        // 2. 配置安全认证(8.x默认开启)
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        credentialsProvider.setCredentials(
                AuthScope.ANY,
                new UsernamePasswordCredentials(username, password)
        );

        // 3. 配置SSL(开发环境关闭证书验证)
        RestClientBuilder builder = RestClient.builder(httpHosts)
                .setHttpClientConfigCallback(httpClientBuilder -> {
                    // 设置认证
                    httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
                    // 配置SSL
                    if (ssl.isDisableVerification()) {
                        try {
                            // 创建信任所有证书的SSL上下文(开发用,生产禁用)
                            SSLContext sslContext = SSLContext.getInstance("TLS");
                            sslContext.init(null, new TrustManager[]{new X509TrustManager() {
                                @Override
                                public X509Certificate[] getAcceptedIssuers() {
                                    return new X509Certificate[0];
                                }

                                @Override
                                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                                }

                                @Override
                                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                                }
                            }}, new java.security.SecureRandom());

                            httpClientBuilder.setSSLContext(sslContext);
                            // 禁用主机名验证
                            httpClientBuilder.setSSLHostnameVerifier((hostname, session) -> true);
                        } catch (Exception e) {
                            throw new RuntimeException("配置SSL失败", e);
                        }
                    }
                    return httpClientBuilder;
                })
                // 配置超时时间
                .setRequestConfigCallback(requestConfigBuilder -> {
                    requestConfigBuilder.setConnectTimeout(connectTimeout);
                    requestConfigBuilder.setSocketTimeout(socketTimeout);
                    return requestConfigBuilder;
                });

        // 4. 构建Transport(Java Client依赖RestClientTransport)
        ElasticsearchTransport transport = new RestClientTransport(
                builder.build(),
                new JacksonJsonpMapper() // JSON处理器(Jackson)
        );

        // 5. 构建并返回同步客户端
        ElasticsearchClient client = new ElasticsearchClient(transport);
        log.info("ES 8.x Java Client初始化完成,集群名称:{},服务器地址:{}", clusterName, hosts);
        return client;
    }

    /**
     * 解析ES服务器地址为HttpHost数组
     */
    private HttpHost[] parseHttpHosts() {
        String[] hostArray = hosts.split(",");
        HttpHost[] httpHosts = new HttpHost[hostArray.length];
        for (int i = 0; i < hostArray.length; i++) {
            String host = hostArray[i];
            String[] hostPort = host.split(":");
            if (hostPort.length != 2) {
                throw new RuntimeException("ES地址格式错误:" + host + "(正确格式:host:port)");
            }
            httpHosts[i] = new HttpHost(hostPort[0], Integer.parseInt(hostPort[1]), scheme);
        }
        return httpHosts;
    }
}

3.2 工具类(封装8.x Java Client操作)

8.x Java Client API与HLRC差异较大,需使用官方新API(如CreateIndexRequestSearchRequest的构建方式)。

java 复制代码
package com.es.demo.util;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.Time;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.HighlightField;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
import co.elastic.clients.elasticsearch.indices.DeleteIndexRequest;
import co.elastic.clients.elasticsearch.indices.ExistsRequest;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.util.ObjectBuilder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;

/**
 * ES 8.x Java Client工具类
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class Es8xJavaClientUtil {
    // 注入8.x Java Client(与配置类中Bean名称一致)
    private final ElasticsearchClient es8xElasticsearchClient;

    // 常量定义
    public static final String KEYWORD_SUFFIX = ".keyword";
    public static final Time DEFAULT_TIMEOUT = Time.of(t -> t.time("3s"));

    /**
     * 1. 创建索引(带自定义mapping)
     */
    public boolean createIndex(String indexName, String mapping) throws IOException {
        if (isIndexExist(indexName)) {
            log.warn("索引[{}]已存在,无需重复创建", indexName);
            return false;
        }

        // 构建创建索引请求
        CreateIndexRequest.Builder requestBuilder = CreateIndexRequest.of(b -> b.index(indexName));
        // 设置mapping(若传入)
        if (StringUtils.isNotBlank(mapping)) {
            requestBuilder.mappings(m -> m.withJson(mapping));
        }

        CreateIndexResponse response = es8xElasticsearchClient.indices().create(requestBuilder.build());
        log.info("索引[{}]创建成功,响应状态:{}", indexName, response.acknowledged());
        return response.acknowledged();
    }

    /**
     * 2. 判断索引是否存在
     */
    public boolean isIndexExist(String indexName) throws IOException {
        ExistsRequest request = ExistsRequest.of(b -> b.index(indexName));
        boolean exists = es8xElasticsearchClient.indices().exists(request).value();
        log.debug("索引[{}]存在状态:{}", indexName, exists);
        return exists;
    }

    /**
     * 3. 删除索引
     */
    public boolean deleteIndex(String indexName) throws IOException {
        if (!isIndexExist(indexName)) {
            log.warn("索引[{}]不存在,无需删除", indexName);
            return false;
        }

        DeleteIndexRequest request = DeleteIndexRequest.of(b -> b.index(indexName));
        es8xElasticsearchClient.indices().delete(request);
        log.info("索引[{}]删除成功", indexName);
        return true;
    }

    /**
     * 4. 添加文档(自动生成docId)
     */
    public String addDoc(String indexName, Object data) throws IOException {
        return addDoc(indexName, UUID.randomUUID().toString().replace("-", "").toUpperCase(), data);
    }

    /**
     * 4. 重载:添加文档(自定义docId)
     */
    public String addDoc(String indexName, String docId, Object data) throws IOException {
        // 构建索引请求
        IndexRequest<Object> request = IndexRequest.of(b -> b
                .index(indexName)
                .id(docId)
                .document(data)
                .timeout(DEFAULT_TIMEOUT)
        );

        IndexResponse response = es8xElasticsearchClient.index(request);
        log.info("文档添加成功:索引[{}],docId[{}],响应状态[{}]",
                indexName, response.id(), response.result().jsonValue());
        return response.id();
    }

    /**
     * 5. 根据docId删除文档
     */
    public boolean deleteDocByDocId(String indexName, String docId) throws IOException {
        if (!isDocExist(indexName, docId)) {
            log.warn("文档不存在:索引[{}],docId[{}]", indexName, docId);
            return false;
        }

        DeleteRequest request = DeleteRequest.of(b -> b
                .index(indexName)
                .id(docId)
                .timeout(DEFAULT_TIMEOUT)
        );

        DeleteResponse response = es8xElasticsearchClient.delete(request);
        log.info("文档删除成功:索引[{}],docId[{}],响应状态[{}]",
                indexName, docId, response.result().jsonValue());
        return true;
    }

    /**
     * 6. 根据docId更新文档(部分更新)
     */
    public boolean updateDocByDocId(String indexName, String docId, Map<String, Object> updateData) throws IOException {
        if (!isDocExist(indexName, docId)) {
            log.warn("文档不存在:索引[{}],docId[{}],无法更新", indexName, docId);
            return false;
        }

        // 构建更新请求(传入Map类型的更新数据)
        UpdateRequest<Object, Object> request = UpdateRequest.of(b -> b
                .index(indexName)
                .id(docId)
                .doc(updateData)
                .timeout(DEFAULT_TIMEOUT)
                .refresh(r -> r.waitFor(true)) // 实时刷新
        );

        UpdateResponse<Object> response = es8xElasticsearchClient.update(request, Object.class);
        log.info("文档更新成功:索引[{}],docId[{}],响应状态[{}]",
                indexName, docId, response.result().jsonValue());
        return true;
    }

    /**
     * 7. 根据docId查询文档(返回Map)
     */
    public Map<String, JsonData> getDocByDocId(String indexName, String docId) throws IOException {
        if (!isDocExist(indexName, docId)) {
            log.warn("文档不存在:索引[{}],docId[{}]", indexName, docId);
            return null;
        }

        GetRequest request = GetRequest.of(b -> b
                .index(indexName)
                .id(docId)
        );

        GetResponse<Object> response = es8xElasticsearchClient.get(request, Object.class);
        // 转换为Map<String, JsonData>(方便解析)
        return response.sourceAsMap();
    }

    /**
     * 8. 判断文档是否存在
     */
    public boolean isDocExist(String indexName, String docId) throws IOException {
        ExistsRequest request = ExistsRequest.of(b -> b
                .index(indexName)
                .id(docId)
        );
        boolean exists = es8xElasticsearchClient.exists(request).value();
        log.debug("文档存在状态:索引[{}],docId[{}],存在[{}]", indexName, docId, exists);
        return exists;
    }

    /**
     * 9. 高亮查询(姓名模糊匹配,分页排序)
     */
    public <T> List<T> searchHighlight(String indexName,
                                       String keyword,
                                       Integer pageNum,
                                       Integer pageSize,
                                       String sortField,
                                       Class<T> clazz) throws IOException {
        // 1. 构建查询条件(matchQuery)
        Function<MatchQuery.Builder, ObjectBuilder<MatchQuery>> matchQuery = q -> q
                .field("name")
                .query(FieldValue.of(keyword))
                .analyzer("ik_smart");

        Query query = Query.of(b -> b.match(matchQuery));

        // 2. 构建高亮配置
        HighlightField highlightField = HighlightField.of(b -> b
                .preTags("<span style='color:red'>")
                .postTags("</span>")
                .requireFieldMatch(false)
        );

        Highlight highlight = Highlight.of(b -> b
                .fields("name", highlightField)
        );

        // 3. 构建搜索请求
        SearchRequest request = SearchRequest.of(b -> b
                .index(indexName)
                .query(query)
                .highlight(highlight)
                .from((pageNum - 1) * pageSize)
                .size(pageSize)
                // 配置排序
                .sort(s -> {
                    if (StringUtils.isNotBlank(sortField)) {
                        String sortFieldWithSuffix = sortField.contains(".") ? sortField : sortField + KEYWORD_SUFFIX;
                        s.field(f -> f.field(sortFieldWithSuffix).order(SortOrder.Asc));
                    }
                    return s;
                })
        );

        // 4. 执行查询并解析结果
        SearchResponse<T> response = es8xElasticsearchClient.search(request, clazz);
        List<Hit<T>> hits = response.hits().hits();
        List<T> resultList = new ArrayList<>();

        for (Hit<T> hit : hits) {
            T doc = hit.source();
            // 处理高亮(此处需根据实体类字段手动设置,8.x Java Client需显式映射)
            if (hit.highlight() != null && hit.highlight().containsKey("name")) {
                // 假设实体类有setName方法,通过反射或FastJSON设置高亮值
                String highlightName = hit.highlight().get("name").get(0);
                if (doc instanceof Map) {
                    ((Map<String, Object>) doc).put("name", highlightName);
                } else {
                    // 若为POJO,可通过FastJSON转换后设置
                    String json = com.alibaba.fastjson.JSON.toJSONString(doc);
                    Map<String, Object> map = com.alibaba.fastjson.JSON.parseObject(json);
                    map.put("name", highlightName);
                    doc = com.alibaba.fastjson.JSON.parseObject(com.alibaba.fastjson.JSON.toJSONString(map), clazz);
                }
            }
            resultList.add(doc);
        }

        log.info("高亮查询完成:索引[{}],匹配总数[{}],分页[{}页/{}条]",
                indexName, response.hits().total().value(), pageNum, pageSize);
        return resultList;
    }
}

3.3 实体类(Employee8xJavaClient)

与前两种方式一致,仅用于数据传输。

java 复制代码
package com.es.demo.entity;

import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

/**
 * 员工实体(用于ES 8.x Java Client)
 */
@Data
public class Employee8xJavaClient {
    private String docId; // 文档ID
    private String jobNo; // 工号
    private String name; // 姓名
    private String job; // 岗位
    private Integer age; // 年龄
    private BigDecimal salary; // 薪资
    private Date jobDay; // 入职时间
    private String remark; // 备注
}

3.4 Controller层(测试接口)

调用8.x工具类实现测试接口,注意处理SSL和认证相关异常。

环境提示

  • 开发/测试环境:创建索引时可暂不传mapping,利用ES动态映射快速验证接口连通性;
  • 生产环境:必须传入自定义mapping(当前代码已配置),明确字段类型、分词器及格式规范,且需提前在集群中预创建索引并关闭动态映射,避免线上数据结构混乱。
java 复制代码
package com.es.demo.controller;

import com.es.demo.entity.Employee8xJavaClient;
import com.es.demo.util.Es8xJavaClientUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/java-client8x/employee")
@RequiredArgsConstructor
public class Employee8xJavaClientController {
    private final Es8xJavaClientUtil es8xJavaClientUtil;
    private static final String EMPLOYEE_INDEX = "employee_java_client_8x";

    /**
     * 1. 创建员工索引(带mapping)
     */
    @GetMapping("/index/create")
    public String createIndex() {
        String mapping = "{\n" +
                "  \"properties\": {\n" +
                "    \"jobNo\": {\"type\": \"keyword\"},\n" +
                "    \"name\": {\n" +
                "      \"type\": \"text\",\n" +
                "      \"analyzer\": \"ik_max_word\",\n" +
                "      \"search_analyzer\": \"ik_smart\"\n" +
                "    },\n" +
                "    \"job\": {\"type\": \"keyword\"},\n" +
                "    \"age\": {\"type\": \"integer\"},\n" +
                "    \"salary\": {\"type\": \"double\"},\n" +
                "    \"jobDay\": {\n" +
                "      \"type\": \"date\",\n" +
                "      \"format\": \"yyyy-MM-dd\"\n" +
                "    },\n" +
                "    \"remark\": {\n" +
                "      \"type\": \"text\",\n" +
                "      \"analyzer\": \"ik_smart\"\n" +
                "    }\n" +
                "  }\n" +
                "}";

        try {
            boolean result = es8xJavaClientUtil.createIndex(EMPLOYEE_INDEX, mapping);
            return result ? "索引创建成功" : "索引已存在";
        } catch (IOException e) {
            log.error("创建索引失败", e);
            return "索引创建失败:" + e.getMessage();
        }
    }

    /**
     * 2. 添加员工文档
     */
    @PostMapping("/save")
    public String save(@RequestBody Employee8xJavaClient employee) {
        try {
            String docId = es8xJavaClientUtil.addDoc(EMPLOYEE_INDEX, employee);
            return "员工添加成功,docId:" + docId;
        } catch (IOException e) {
            log.error("添加员工失败", e);
            return "添加员工失败:" + e.getMessage();
        }
    }

    /**
     * 3. 高亮查询员工
     */
    @GetMapping("/search-highlight")
    public List<Employee8xJavaClient> searchHighlight(
            @RequestParam String name,
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize) {
        try {
            // 调用工具类高亮查询(按jobNo排序)
            return es8xJavaClientUtil.searchHighlight(
                    EMPLOYEE_INDEX,
                    name,
                    pageNum,
                    pageSize,
                    "jobNo", // 排序字段
                    Employee8xJavaClient.class // 返回实体类类型
            );
        } catch (IOException e) {
            log.error("高亮查询员工失败(姓名关键词:{})", name, e);
            return null;
        }
    }

    /**
     * 4. 测试示例:添加单个员工
     */
    @GetMapping("/test/save")
    public String 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) {
        Employee8xJavaClient employee = new Employee8xJavaClient();
        // 赋值请求参数到员工实体
        employee.setJobNo(jobNo);
        employee.setName(name);
        employee.setJob(job);
        employee.setAge(age);
        employee.setSalary(salary);
        employee.setJobDay(jobDay);
        employee.setRemark(remark);
        // docId留空,由工具类自动生成UUID

        try {
            String docId = es8xJavaClientUtil.addDoc(EMPLOYEE_INDEX, employee);
            return "测试添加员工成功,docId:" + docId;
        } catch (IOException e) {
            log.error("测试添加员工失败", e);
            return "测试添加员工失败:" + e.getMessage();
        }
    }
}

4. 测试步骤

  1. 启动ES 8.x服务器(确保9200端口可访问,且已记录初始化时生成的elastic账号密码,需与application.yml配置一致);
  2. 启动SpringBoot项目(若启动时出现SSL相关异常,检查application.yml中ssl.disable-verification: true配置是否生效);
  3. 调用创建索引接口:访问http://localhost:8083/api/java-client8x/employee/index/create,返回"索引创建成功"即可;
  4. 调用测试添加接口:访问http://localhost:8083/api/java-client8x/employee/test/save?jobNo=2024003\&name=王五\&job=后端开发\&age=30\&salary=28000\&jobDay=2022-05-10\&remark=架构师,获取返回的docId;
  5. 调用高亮查询接口:访问http://localhost:8083/api/java-client8x/employee/search-highlight?name=王\&pageNum=1\&pageSize=10,验证返回结果中`name`字段是否有红色高亮标签,且数据匹配正确。
相关推荐
Miraitowa_cheems3 小时前
LeetCode算法日记 - Day 82: 环形子数组的最大和
java·数据结构·算法·leetcode·决策树·线性回归·深度优先
豐儀麟阁贵4 小时前
4.5数组排序算法
java·开发语言·数据结构·算法·排序算法
Halo_tjn4 小时前
Java Map集合
java·开发语言·计算机
程序猿小蒜5 小时前
基于springboot的车辆管理系统设计与实现
java·数据库·spring boot·后端·spring·oracle
zl9798996 小时前
SpringBoot-Web开发之Web原生组件注入
java·spring boot·spring
小羊学伽瓦6 小时前
【Java数据结构】——常见力扣题综合
java·数据结构·leetcode·1024程序员节
I'm Jie6 小时前
(五)Gradle 依赖传递与冲突处理
java·spring boot·spring·kotlin·gradle·maven
我命由我123456 小时前
Spring Cloud - Spring Cloud 声明式接口调用(Fiegn 声明式接口调用概述、Fiegn 使用)
java·后端·spring·spring cloud·微服务·架构·java-ee
_extraordinary_6 小时前
Java Spring事务,事务的传播机制
java·开发语言·spring