Elastic实战:彻底解决spring-data-elasticsearch查询结果size大于0但显示为空问题

简介: 我们在使用spring-data-elasticsearch,可能会出现查询结果为null,但返回的数据size是大于0的。或者某一部分字段有值,某一部分字段为null其结果如下图所示,那么这个问题是怎么产生的呢?今天我们就来详细解析

0. 引言

我们在使用spring-data-elasticsearch,可能会出现查询结果为null,但返回的数据size是大于0的。或者某一部分字段有值,某一部分字段为null

其结果如下图所示,那么这个问题是怎么产生的呢?今天我们就来详细解析
在这里插入图片描述

1. 问题分析

因为我们使用的是spring-data-elasticsearch,在实体类中已经用@Field注解声明了实体类与es索引mapping之间的映射关系,但是从查询结果来看,数据是已经查询出来了,不然size也不会大于0.
在这里插入图片描述

那么问题就在于实体类与es索引之间的映射关系没有生效。于是我们下一步就是去探索为什么没有生效的原因。

2.问题解决

2.1 实体类与es mapping中的属性名保持一致

仔细观察数据我们会发现,并不是所有的字段都没有对应上,实体类和索引同名的字段值都是正常显示的,但不同名的就没有正常显示。

那么最简单的解决办法就是将实体类中的属性名与es索引中的属性名保持一致,这样就能直接对应上了

但这个方法我不太推荐,因为有的时候mapping可能已经创建好了并且已经在之前的代码中使用了,不可能再去调整之前的代码。于是我们引入了第二种解决方案

2.2 使用@JsonProperty给实体类属性添加别名

既然是实体类名称与es mapping中的名称不一致,那么我们可以通过添加@JsonProperty注解来给实体类中的属性设置别名,其别名与es mapping保持一致即可
比如

@JsonProperty("project_id”)
private Long projectId;

但这个方法会导致我们输出给前端的属性名称也是@JsonProperty中声明的名称,有的时候我们就是想要使用实体类的名称,而且每个字段中都添加上这么一个注解,也实在太累。不是“懒人”的风格。说到底,并没有解决我们的根本问题:明明已经在实体类中@Field注解上声明了es mapping中的字段名,为什么不能利用@Field注解来自动转换呢?

2.3 使用ElasticsearchEntityMapper替换默认的EntityMapper

在实现第三种方案前,我们先把问题的原因分析清楚,先找到spring-data-elasticsearch将查询到的es结果与实体类对应的代码位置

我们从源头开始追溯,我们是通过调用queryForPage方法来实现查询的,那么找到该方法的源码
在这里插入图片描述
从方法名称可以猜出其通过一个mapper,mapResults方法来实现索引与实体类的映射,我们点击进mapResults方法看看
在这里插入图片描述
发现这是一个接口类,该接口下有两个实现类,我们点击进默认的DefaultResultMapper去看看
在这里插入图片描述
会发现该类的mapResults方法下使用了一个mapEntity方法来进行索引与实体类的映射
(不常阅读源码的同学可能会有疑惑,你怎么知道这个方法是用来做索引和实体类的映射的?其实先是凭借经验去猜,我们通过查看方法名也能猜的八九不离十了,mapEntity,译为映射实体类。,还要更直白些吗?这也提示我们,写书代码时方法名一定要能反映业务逻辑)
在这里插入图片描述
进入该方法,发现这是个接口类,并且调用了getEntityMapper()方法的mapToObject方法。不用说这个mapToObject方法一定是映射方法了
在这里插入图片描述
该方法是一个EntityMapper接口提供,该接口下提供了两个实现类
在这里插入图片描述
这个时候,有经验的同学可能已经嗅到一丝气息了。从这个命名来看,DefaultEntityMapper明显是默认的EntityMapper,也就是说默认是使用该类提供的映射方法。而ElasticsearchEntityMapper才是es相关的映射方法

这个时候其实问题已经解决大部分了,如果你不想继续阅读源码的话,可以直接搜索该类的作用已经两个实现类之间的区别。很快就能得出答案。

之后的代码比较晦涩且链路较长,我就不带大家详细解读了,给出调用过程,有兴趣的同学可以自己去翻翻
在这里插入图片描述
从该调用过程可以知道,ElasticsearchEntityMapper中使用识别@Field注解的,而DefaultEntityMapper会忽略该注解,因此我们需要做的就是在项目中显示声明使用ElasticsearchEntityMapper

配置方法也不难,就是创建一个配置类,在elasticsearchRestTemplate这个Bean的声明方法中将ElasticsearchEntityMapper作为参数传入。后续使用elasticsearchRestTemplate时就会使用ElasticsearchEntityMapper作为映射方法提供类

同时声明注解@EnableElasticsearchRepositories(basePackages = "com.example.esdemo"),表明spring-data-elasticsearch方式声明的Repositories类也将使用ElasticsearchEntityMapper

package org.springblade.statistic.conf;
 
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.support.DefaultConversionService; 
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
import org.springframework.data.elasticsearch.config.AbstractElasticsearchConfiguration;
import org.springframework.data.elasticsearch.core.ElasticsearchEntityMapper;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.EntityMapper; 
import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;
import org.springframework.http.HttpHeaders; 
/**
 * @author whx
 * @date 2022/5/23
 */
@Configuration
@EnableElasticsearchRepositories(basePackages = "com.example.esdemo")
public class ElasticRestClientConfig extends AbstractElasticsearchConfiguration {

    @Value("${spring.elasticsearch.rest.uris}")
    private String url;
    @Value("${spring.elasticsearch.rest.username}")
    private String username;
    @Value("${spring.elasticsearch.rest.password}")
    private String password;

    @Override
    @Bean
    public RestHighLevelClient elasticsearchClient() {
        url = url.replace("http://","");
        HttpHeaders headers = new HttpHeaders();
        headers.setBasicAuth(username,password);
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
            .connectedTo(url)
            .withDefaultHeaders(headers)
            .build();
        return RestClients.create(clientConfiguration).rest();
    }

    @Bean
    public ElasticsearchRestTemplate elasticsearchRestTemplate(RestHighLevelClient elasticsearchClient,EntityMapper entityMapper){
        return new ElasticsearchRestTemplate(elasticsearchClient,entityMapper);
    }

    /**
     * 指定EntityMapper为ElasticsearchEntityMapper
     * 解决es mapper映射实体类问题
     * @return
     */
    @Bean
    @Override
    public EntityMapper entityMapper() {
        ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(), new DefaultConversionService());
        entityMapper.setConversions(elasticsearchCustomConversions());
        return entityMapper;
    }
}

如果你觉得本文对你的学习有帮助的话,不妨点赞支持一下吧

关注公众号 Elasticsearch之家,掌握更多新鲜内容

相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。  
目录
相关文章
|
8月前
|
缓存 监控 前端开发
顺企网 API 开发实战:搜索 / 详情接口从 0 到 1 落地(附 Elasticsearch 优化 + 错误速查)
企业API开发常陷参数、缓存、错误处理三大坑?本指南拆解顺企网双接口全流程,涵盖搜索优化、签名验证、限流应对,附可复用代码与错误速查表,助你2小时高效搞定开发,提升响应速度与稳定性。
|
9月前
|
负载均衡 监控 Java
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
本文详细介绍了 Spring Cloud Gateway 的核心功能与实践配置。首先讲解了网关模块的创建流程,包括依赖引入(gateway、nacos 服务发现、负载均衡)、端口与服务发现配置,以及路由规则的设置(需注意路径前缀重复与优先级 order)。接着深入解析路由断言,涵盖 After、Before、Path 等 12 种内置断言的参数、作用及配置示例,并说明了自定义断言的实现方法。随后重点阐述过滤器机制,区分路由过滤器(如 AddRequestHeader、RewritePath、RequestRateLimiter 等)与全局过滤器的作用范围与配置方式,提
Spring Cloud Gateway 全解析:路由配置、断言规则与过滤器实战指南
|
10月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
1438 3
|
人工智能 搜索推荐 Java
Spring AI与DeepSeek实战三:打造企业知识库
本文基于Spring AI与RAG技术结合,通过构建实时知识库增强大语言模型能力,实现企业级智能搜索场景与个性化推荐,攻克LLM知识滞后与生成幻觉两大核心痛点。
1579 7
|
8月前
|
监控 Cloud Native Java
Spring Boot 3.x 微服务架构实战指南
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Spring Boot 3.x与微服务架构,探索云原生、性能优化与高可用系统设计。以代码为笔,在二进制星河中谱写极客诗篇。关注我,共赴技术星辰大海!(238字)
1331 2
Spring Boot 3.x 微服务架构实战指南
|
8月前
|
XML Java 测试技术
《深入理解Spring》:IoC容器核心原理与实战
Spring IoC通过控制反转与依赖注入实现对象间的解耦,由容器统一管理Bean的生命周期与依赖关系。支持XML、注解和Java配置三种方式,结合作用域、条件化配置与循环依赖处理等机制,提升应用的可维护性与可测试性,是现代Java开发的核心基石。
|
人工智能 Java API
Spring AI 实战|Spring AI入门之DeepSeek调用
本文介绍了Spring AI框架如何帮助Java开发者轻松集成和使用大模型API。文章从Spring AI的初探开始,探讨了其核心能力及应用场景,包括手动与自动发起请求、流式响应实现打字机效果,以及兼容不同AI服务(如DeepSeek、通义千问)的方法。同时,还详细讲解了如何在生产环境中添加监控以优化性能和成本管理。通过Spring AI,开发者可以简化大模型调用流程,降低复杂度,为企业智能应用开发提供强大支持。最后,文章展望了Spring AI在未来AI时代的重要作用,鼓励开发者积极拥抱这一技术变革。
4424 71
Spring AI 实战|Spring AI入门之DeepSeek调用
|
安全 Java 数据库
Spring Security 实战指南:从入门到精通
本文详细介绍了Spring Security在Java Web项目中的应用,涵盖登录、权限控制与安全防护等功能。通过Filter Chain过滤器链实现请求拦截与认证授权,核心组件包括AuthenticationProvider和UserDetailsService,负责用户信息加载与密码验证。文章还解析了项目结构,如SecurityConfig配置类、User实体类及自定义登录逻辑,并探讨了Method-Level Security、CSRF防护、Remember-Me等进阶功能。最后总结了Spring Security的核心机制与常见配置,帮助开发者构建健壮的安全系统。
2386 0
|
10月前
|
人工智能 监控 安全
如何快速上手【Spring AOP】?核心应用实战(上篇)
哈喽大家好吖~欢迎来到Spring AOP系列教程的上篇 - 应用篇。在本篇,我们将专注于Spring AOP的实际应用,通过具体的代码示例和场景分析,帮助大家掌握AOP的使用方法和技巧。而在后续的下篇中,我们将深入探讨Spring AOP的实现原理和底层机制。 AOP(Aspect-Oriented Programming,面向切面编程)是Spring框架中的核心特性之一,它能够帮助我们解决横切关注点(如日志记录、性能统计、安全控制、事务管理等)的问题,提高代码的模块化程度和复用性。