解决 Spring Boot 对 Elasticsearch 字段没有小驼峰映射的问题

场景重现

在使用 MyBatis/Mybatis-Plus 框架对 MySQL 操作时习惯了字段名小驼峰映射,然而在操作 Elasticsearch 时发现字段名没有小驼峰映射。

解决方法

1. 使用 `ObjectMapper` 手动转换:

这是最直接也最常用的方法。 在 Spring Boot 应用中使用 `ObjectMapper` 将从 Elasticsearch 获取的数据进行转换,将下划线命名转换为小驼峰命名。

java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class ElasticsearchService {

    @Autowired
    private ObjectMapper objectMapper; // 通过 @Bean 配置 ObjectMapper

    public Map<String, Object> getDocument(String index, String id) {
        // 假设你已经有了从 Elasticsearch 获取文档的方法,这里是模拟
        Map<String, Object> document = new HashMap<>();
        document.put("first_name", "John");
        document.put("last_name", "Doe");

        // 将下划线命名转换为小驼峰命名
        ObjectMapper snakeCaseMapper = new ObjectMapper();
        snakeCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        ObjectMapper camelCaseMapper = new ObjectMapper();
        camelCaseMapper.setPropertyNamingStrategy(PropertyNamingStrategies.LOWER_CAMEL_CASE);

        try {
            String jsonString = snakeCaseMapper.writeValueAsString(document);
            return camelCaseMapper.readValue(jsonString, Map.class);

        } catch (Exception e) {
            // 处理异常
            e.printStackTrace();
            return null;
        }
    }
}

解释:

• `ObjectMapper` 配置: 在 Spring Boot 应用中,你需要配置一个 `ObjectMapper` Bean。

• `PropertyNamingStrategy`: 通过设置 `PropertyNamingStrategy`,`ObjectMapper` 可以自动将下划线命名转换为小驼峰命名。 这里先使用`snakeCaseMapper`读取,再使用`camelCaseMapper`写出。

• 转换过程: 从 Elasticsearch 获取的数据(`Map`)使用 `ObjectMapper`序列化成 JSON 字符串,然后再反序列化成 `Map`。 在这个过程中,`PropertyNamingStrategy` 会自动进行命名转换。

配置 `ObjectMapper` Bean:

java 复制代码
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ObjectMapperConfig {

    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        // 可以根据需要配置 ObjectMapper 的其他属性
        return objectMapper;
    }
}

2. 使用 Elasticsearch 的 `field_alias` (推荐但ES版本要支持):

从 Elasticsearch 7.0 开始,引入了 `field_alias`。 你可以利用这个功能,在 Elasticsearch 索引的 Mapping 中定义字段别名,将下划线命名的字段映射到小驼峰命名的字段。

java 复制代码
PUT my_index
{
  "mappings": {
    "properties": {
      "first_name": {
        "type": "alias",
        "path": "first_name"
      },
      "last_name": {
        "type": "alias",
        "path": "last_name"
      }
    }
  }
}

注意: `field_alias` 只是别名,它仍然需要你存储实际的下划线命名的字段。 这样做的好处是,你可以在查询时使用小驼峰命名,但底层存储仍然是下划线命名。

3. 自定义 Elasticsearch 客户端 (不推荐):

你可以自定义 Elasticsearch 客户端,拦截响应结果,并手动进行字段命名转换。 这通常比较复杂,需要你深入了解 Elasticsearch 客户端的内部机制。不建议使用这种方法,因为它维护成本高。

4. 定义 DTO 类并使用 `@JsonProperty` 注解:

创建一个 DTO 类,对应 Elasticsearch 中的文档结构,并在 DTO 类的字段上使用 `@JsonProperty` 注解,将 Elasticsearch 的字段名映射到 DTO 类的字段上。

java 复制代码
import com.fasterxml.jackson.annotation.JsonProperty;

public class UserDTO {

    @JsonProperty("first_name")
    private String firstName;

    @JsonProperty("last_name")
    private String lastName;

    // Getters and setters
}

然后,从 Elasticsearch 获取数据后,将数据映射到 `UserDTO` 对象。

5. 使用 Elasticsearch 插件 (不推荐):

有一些第三方 Elasticsearch 插件可以自动进行字段命名转换。 但使用插件会增加系统的复杂性,并且可能存在兼容性问题,因此不建议使用。

选择哪种方法?

  • 优先考虑 field_alias (如果你的 Elasticsearch 版本支持): 它是最优雅的解决方案,对现有代码的改动最小。
  • ObjectMapper 手动转换: 如果你无法修改 Elasticsearch 的 Mapping,那么使用 ObjectMapper 是一个不错的选择。 它简单易用,并且可以灵活地控制转换过程。
  • @JsonProperty 注解: 如果你的数据结构相对固定,并且需要将其映射到 DTO 对象,那么使用 @JsonProperty 注解是一个好主意。
  • 避免自定义客户端和插件: 除非你有非常特殊的需求,否则不建议使用自定义客户端和插件,因为它们维护成本很高。
相关推荐
十八旬1 分钟前
苍穹外卖项目实战(day7-1)-缓存菜品和缓存套餐功能-记录实战教程、问题的解决方法以及完整代码
java·数据库·spring boot·redis·缓存·spring cache
会豪5 分钟前
工业仿真(simulation)--发生器,吸收器,缓冲区(2)
后端
SamDeepThinking5 分钟前
使用Cursor生成【财务对账系统】前后端代码
后端·ai编程·cursor
饭碗的彼岸one6 分钟前
C++ 并发编程:异步任务
c语言·开发语言·c++·后端·c·异步
会豪11 分钟前
工业仿真(simulation)--仿真引擎,离散事件仿真(1)
后端
Java微观世界12 分钟前
匿名内部类和 Lambda 表达式为何要求外部变量是 final 或等效 final?原理与解决方案
java·后端
SimonKing30 分钟前
全面解决中文乱码问题:从诊断到根治
java·后端·程序员
在未来等你1 小时前
Elasticsearch面试精讲 Day 15:索引别名与零停机更新
大数据·分布式·elasticsearch·搜索引擎·面试
几颗流星1 小时前
Java 中使用 CountDownLatch 增加线程竞争,帮助复现并发问题
后端
郑洁文1 小时前
基于SpringBoot的天气预报系统的设计与实现
java·spring boot·后端·毕设