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

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: 我们在使用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之家,掌握更多新鲜内容

相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
目录
相关文章
|
4天前
|
XML Java 测试技术
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
这篇文章介绍了Spring5框架的三个新特性:支持@Nullable注解以明确方法返回、参数和属性值可以为空;引入函数式风格的GenericApplicationContext进行对象注册和管理;以及如何整合JUnit5进行单元测试,同时讨论了JUnit4与JUnit5的整合方法,并提出了关于配置文件加载的疑问。
Spring5入门到实战------17、Spring5新功能 --Nullable注解和函数式注册对象。整合JUnit5单元测试框架
|
4天前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
4天前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
1天前
|
SQL XML Java
Spring5入门到实战------12、使用JdbcTemplate操作数据库(增删改查)。具体代码+讲解 【上篇】
这篇文章是Spring5框架的实战教程,详细讲解了如何使用JdbcTemplate进行数据库的增删改查操作,包括在项目中引入依赖、配置数据库连接池、创建实体类、定义DAO接口及其实现,并提供了具体的代码示例和测试结果,最后还提供了完整的XML配置文件和测试代码。
Spring5入门到实战------12、使用JdbcTemplate操作数据库(增删改查)。具体代码+讲解 【上篇】
|
1天前
|
NoSQL Java Redis
Redis6入门到实战------ 八、Redis与Spring Boot整合
这篇文章详细介绍了如何在Spring Boot项目中整合Redis,包括在`pom.xml`中添加依赖、配置`application.properties`文件、创建配置类以及编写测试类来验证Redis的连接和基本操作。
Redis6入门到实战------ 八、Redis与Spring Boot整合
|
4天前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
4天前
|
SQL 数据库
Spring5入门到实战------13、使用JdbcTemplate操作数据库(批量增删改)。具体代码+讲解 【下篇】
这篇文章是Spring5框架的实战教程,深入讲解了如何使用JdbcTemplate进行数据库的批量操作,包括批量添加、批量修改和批量删除的具体代码实现和测试过程,并通过完整的项目案例展示了如何在实际开发中应用这些技术。
Spring5入门到实战------13、使用JdbcTemplate操作数据库(批量增删改)。具体代码+讲解 【下篇】
|
4天前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
4天前
|
XML Java 数据格式
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
这篇文章是Spring5框架的入门教程,详细讲解了IOC容器中Bean的自动装配机制,包括手动装配、`byName`和`byType`两种自动装配方式,并通过XML配置文件和Java代码示例展示了如何在Spring中实现自动装配。
Spring5入门到实战------6、IOC容器-Bean管理XML方式(自动装配)
|
4天前
|
XML Java 数据格式
Spring5入门到实战------5、IOC容器-Bean管理(三)
这篇文章深入探讨了Spring5框架中IOC容器的高级Bean管理,包括FactoryBean的使用、Bean作用域的设置、Bean生命周期的详细解释以及Bean后置处理器的实现和应用。
Spring5入门到实战------5、IOC容器-Bean管理(三)